Example #1
0
        public static void Perform()
        {
            var doc = Document.Current;

            foreach (var row in doc.TopLevelSelectedRows().ToList())
            {
                if (!row.IsCopyPasteAllowed())
                {
                    continue;
                }
                if (row.Components.Get <PropertyRow>()?.Animator is IAnimator animator)
                {
                    doc.History.DoTransaction(() => {
                        foreach (var keyframe in animator.Keys.ToList())
                        {
                            RemoveKeyframe.Perform(animator, keyframe.Frame);
                        }
                    });
                }
                else if (row.Components.Get <AnimationTrackRow>()?.Track is AnimationTrack track)
                {
                    doc.History.DoTransaction(() => {
                        RemoveFromList <AnimationTrackList, AnimationTrack> .Perform(doc.Animation.Tracks, track);
                    });
                }
                else if (row.Components.Get <BoneRow>()?.Bone is Bone currentBone)
                {
                    var bones          = doc.Container.Nodes.OfType <Bone>().ToList();
                    var dependentBones = BoneUtils.FindBoneDescendats(currentBone, bones).ToList();
                    dependentBones.Insert(0, currentBone);
                    UntieWidgetsFromBones.Perform(dependentBones, doc.Container.Nodes.OfType <Widget>());
                    foreach (var bone in dependentBones)
                    {
                        UnlinkFolderItem.Perform(doc.Container, bone);
                        doc.Container.AsWidget.BoneArray[bone.Index] = default;
                    }
                    doc.History.DoTransaction(() => {
                        foreach (var mesh in doc.Container.Nodes.OfType <Lime.Animesh>())
                        {
                            mesh.OnBoneArrayChanged?.Invoke();
                        }
                    });
                }
                else if (Row.GetFolderItem(row) is IFolderItem item)
                {
                    UnlinkFolderItem.Perform(doc.Container, item);
                }
            }
        }
Example #2
0
        public static void Perform(Bone boneInChain)
        {
            var bones      = Document.Current.Container.Nodes.OfType <Bone>();
            var rootParent = bones.GetBone(boneInChain.BaseIndex);

            while (rootParent != null && rootParent.BaseIndex != 0)
            {
                rootParent = bones.GetBone(rootParent.BaseIndex);
            }
            var tree = BoneUtils.SortBones(BoneUtils.FindBoneDescendats(rootParent ?? boneInChain, bones));
            var loc  = Document.Current.Container.RootFolder().Find(rootParent ?? boneInChain);

            foreach (var child in tree)
            {
                MoveNodes.Perform(child, new FolderItemLocation(loc.Folder, ++loc.Index));
            }
        }
Example #3
0
        private static void CopyNodes(System.IO.MemoryStream stream)
        {
            var frame = new Frame();

            foreach (var row in Document.Current.TopLevelSelectedRows())
            {
                if (!row.IsCopyPasteAllowed())
                {
                    continue;
                }
                var bone = row.Components.Get <BoneRow>()?.Bone;
                if (bone != null)
                {
                    var c = (Bone)Document.CreateCloneForSerialization(bone);
                    c.BaseIndex = 0;
                    frame.RootFolder().Items.Add(c);
                    if (!bone.EditorState().ChildrenExpanded)
                    {
                        var children = BoneUtils.FindBoneDescendats(bone, Document.Current.Container.Nodes.OfType <Bone>());
                        foreach (var b in children)
                        {
                            c = (Bone)Document.CreateCloneForSerialization(b);
                            frame.RootFolder().Items.Add(c);
                        }
                    }
                    continue;
                }
                var node = row.Components.Get <NodeRow>()?.Node;
                if (node != null)
                {
                    frame.RootFolder().Items.Add(Document.CreateCloneForSerialization(node));
                }
                var folder = row.Components.Get <FolderRow>()?.Folder;
                if (folder != null)
                {
                    frame.RootFolder().Items.Add(CloneFolder(folder));
                }
                var animator = Cloner.Clone(row.Components.Get <PropertyRow>()?.Animator);
                if (animator != null)
                {
                    frame.Animators.Add(animator);
                }
            }
            frame.SyncFolderDescriptorsAndNodes();
            TangerinePersistence.Instance.WriteObject(null, stream, frame, Persistence.Format.Json);
        }
Example #4
0
        static Frame CreateInMemoryCopy()
        {
            var frame = new Frame();

            foreach (var row in Document.Current.TopLevelSelectedRows())
            {
                if (!row.IsCopyPasteAllowed())
                {
                    continue;
                }
                var bone = row.Components.Get <BoneRow>()?.Bone;
                if (bone != null)
                {
                    var c = (Bone)Document.CreateCloneForSerialization(bone);
                    c.BaseIndex = 0;
                    frame.RootFolder().Items.Add(c);
                    if (!bone.EditorState().ChildrenExpanded)
                    {
                        var children = BoneUtils.FindBoneDescendats(bone, Document.Current.Container.Nodes.OfType <Bone>());
                        foreach (var b in children)
                        {
                            c = (Bone)Document.CreateCloneForSerialization(b);
                            frame.RootFolder().Items.Add(c);
                        }
                    }
                    continue;
                }
                var node = row.Components.Get <NodeRow>()?.Node;
                if (node != null)
                {
                    frame.RootFolder().Items.Add(Document.CreateCloneForSerialization(node));
                }
                var folder = row.Components.Get <FolderRow>()?.Folder;
                if (folder != null)
                {
                    frame.RootFolder().Items.Add(CloneFolder(folder));
                }
                var animator = row.Components.Get <PropertyRow>()?.Animator.Clone();
                if (animator != null)
                {
                    frame.Animators.Add(animator);
                }
            }
            frame.SyncFolderDescriptorsAndNodes();
            return(frame);
        }
Example #5
0
 public static void Perform()
 {
     foreach (var row in Document.Current.TopLevelSelectedRows().ToList())
     {
         var item = Row.GetFolderItem(row);
         if (item != null)
         {
             UnlinkFolderItem.Perform(Document.Current.Container, item);
         }
         var root = row.Components.Get <BoneRow>()?.Bone;
         if (root != null)
         {
             var bones = Document.Current.Container.Nodes.OfType <Bone>().ToList();
             foreach (var bone in BoneUtils.FindBoneDescendats(root, bones))
             {
                 UnlinkFolderItem.Perform(Document.Current.Container, bone);
             }
         }
     }
 }
Example #6
0
 public static void Perform()
 {
     foreach (var row in Document.Current.TopLevelSelectedRows().ToList())
     {
         if (!row.IsCopyPasteAllowed())
         {
             continue;
         }
         if (row.Components.Get <PropertyRow>()?.Animator is IAnimator animator)
         {
             Document.Current.History.DoTransaction(() => {
                 foreach (var keyframe in animator.Keys.ToList())
                 {
                     RemoveKeyframe.Perform(animator, keyframe.Frame);
                 }
             });
             continue;
         }
         var item        = Row.GetFolderItem(row);
         var currentBone = row.Components.Get <BoneRow>()?.Bone;
         if (currentBone != null)
         {
             var bones          = Document.Current.Container.Nodes.OfType <Bone>().ToList();
             var dependentBones = BoneUtils.FindBoneDescendats(currentBone, bones).ToList();
             dependentBones.Insert(0, currentBone);
             UntieWidgetsFromBones.Perform(dependentBones, Document.Current.Container.Nodes.OfType <Widget>());
             foreach (var bone in dependentBones)
             {
                 UnlinkFolderItem.Perform(Document.Current.Container, bone);
                 Document.Current.Container.AsWidget.BoneArray[bone.Index] = default(BoneArray.Entry);
             }
         }
         else if (item != null)
         {
             UnlinkFolderItem.Perform(Document.Current.Container, item);
         }
     }
 }
Example #7
0
        private static BoneNode GetDirectSingleChildOf([NotNull] BoneNode b)
        {
            var l = new List <BoneNode>();

            foreach (var c in b.Children)
            {
                var isGenerated = BoneUtils.IsNameGenerated(c.Path);

                if (!isGenerated)
                {
                    l.Add(c);
                }
            }

            if (l.Count == 1)
            {
                return(l[0]);
            }
            else
            {
                return(null);
            }
        }
Example #8
0
        private void ReorderBonesRecursive(Widget widget)
        {
            if (widget == null)
            {
                return;
            }
            var allBones = widget.Nodes.OfType <Bone>();

            foreach (var root in allBones.Where(b => b.BaseIndex == 0))
            {
                var bones = BoneUtils.SortBones(BoneUtils.FindBoneDescendats(root, allBones), reverseOrder: true);
                var loc   = widget.Nodes.IndexOf(root);
                foreach (var bone in bones)
                {
                    bone.Unlink();
                    widget.AsWidget.Nodes.Insert(loc++, bone);
                }
            }

            foreach (var child in widget.Nodes)
            {
                ReorderBonesRecursive(child.AsWidget);
            }
        }
Example #9
0
        public static bool Perform(string data, RowLocation location)
        {
            if (!CanPaste(data, location))
            {
                return(false);
            }
            Frame frame;

            try {
                var stream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(data));
                frame = Serialization.ReadObject <Frame>(Document.Current.Path, stream);
            } catch (System.Exception e) {
                Debug.Write(e);
                return(false);
            }
            FolderItemLocation folderLocation;

            if (location.ParentRow.Rows.Count > 0)
            {
                folderLocation = Row.GetFolderItemLocation(location.ParentRow.Rows[location.Index]);
                folderLocation.Index++;
            }
            else
            {
                folderLocation = new FolderItemLocation {
                    Index = 0, Folder = location.ParentRow.Components.Get <FolderRow>().Folder
                };
            }
            if (!folderLocation.Folder.Expanded)
            {
                SetProperty.Perform(folderLocation.Folder, nameof(Folder.Expanded), true);
            }
            var items = frame.RootFolder().Items.ToList();

            foreach (var n in items.OfType <Node>())
            {
                Document.Current.Decorate(n);
            }
            frame.RootFolder().Items.Clear();
            frame.RootFolder().SyncDescriptorsAndNodes(frame);
            ClearRowSelection.Perform();
            while (items.Count > 0)
            {
                var item = items.First();
                var bone = item as Bone;
                if (bone != null)
                {
                    if (bone.BaseIndex != 0)
                    {
                        continue;
                    }
                    var newIndex = Document.Current.Container.Nodes.OfType <Bone>().Max(b => b.Index) + 1;
                    var children = BoneUtils.FindBoneDescendats(bone, items.OfType <Bone>()).ToList();
                    var map      = new Dictionary <int, int>();
                    map.Add(bone.Index, newIndex);
                    bone.BaseIndex = location.ParentRow.Components.Get <BoneRow>()?.Bone.Index ?? 0;
                    bone.Index     = newIndex;
                    InsertFolderItem.Perform(
                        Document.Current.Container,
                        folderLocation, bone);
                    folderLocation.Index++;
                    foreach (var b in children)
                    {
                        b.BaseIndex = map[b.BaseIndex];
                        map.Add(b.Index, b.Index = ++newIndex);
                        InsertFolderItem.Perform(
                            Document.Current.Container,
                            folderLocation, b);
                        folderLocation.Index++;
                        items.Remove(b);
                    }
                    Document.Current.Container.RootFolder().SyncDescriptorsAndNodes(Document.Current.Container);
                    SortBonesInChain.Perform(bone);
                    SelectRow.Perform(Document.Current.GetRowForObject(item));
                }
                else
                {
                    if (!location.ParentRow.Components.Contains <BoneRow>())
                    {
                        InsertFolderItem.Perform(
                            Document.Current.Container,
                            folderLocation, item);
                        folderLocation.Index++;
                        SelectRow.Perform(Document.Current.GetRowForObject(item));
                    }
                }
                items.Remove(item);
            }
            return(true);
        }
Example #10
0
        private static IReadOnlyList <PmxBone> AddBones([NotNull] Avatar combinedAvatar, [NotNull] Mesh combinedMesh, [NotNull, ItemNotNull] IReadOnlyList <PmxVertex> vertices)
        {
            var boneCount = combinedAvatar.AvatarSkeleton.NodeIDs.Length;
            var bones     = new List <PmxBone>(boneCount);

            var hierachy = BoneUtils.BuildBoneHierarchy(combinedAvatar);

            for (var i = 0; i < boneCount; ++i)
            {
                var bone      = new PmxBone();
                var transform = combinedAvatar.AvatarSkeletonPose.Transforms[i];
                var boneNode  = hierachy[i];

                var pmxBoneName = BoneUtils.GetPmxBoneName(boneNode.Path);

                bone.Name        = pmxBoneName;
                bone.NameEnglish = BoneUtils.TranslateBoneName(pmxBoneName);

                // PMX's bone positions are in world coordinate system.
                // Unity's are in local coords.
                bone.InitialPosition = boneNode.InitialPositionWorld;
                bone.CurrentPosition = bone.InitialPosition;

                bone.ParentIndex = boneNode.Parent?.Index ?? -1;
                bone.BoneIndex   = i;

                var singleDirectChild = GetDirectSingleChildOf(boneNode);

                if (singleDirectChild != null)
                {
                    bone.SetFlag(BoneFlags.ToBone);
                    bone.To_Bone = singleDirectChild.Index;
                }
                else
                {
                    // TODO: Fix this; it should point to a world position.
                    bone.To_Offset = transform.Translation.ToOpenTK().FixUnityToOpenTK();
                }

                // No use. This is just a flag to specify more details to rotation/translation limitation.
                //bone.SetFlag(BoneFlags.LocalFrame);
                bone.InitialRotation = transform.Rotation.ToOpenTK().FixUnityToOpenTK();
                bone.CurrentRotation = bone.InitialRotation;

                //bone.Level = boneNode.Level;
                bone.Level = 0;

                if (BoneUtils.IsBoneMovable(boneNode.Path))
                {
                    bone.SetFlag(BoneFlags.Rotation | BoneFlags.Translation);
                }
                else
                {
                    bone.SetFlag(BoneFlags.Rotation);
                }

                if (ConversionConfig.Current.HideUnityGeneratedBones)
                {
                    if (BoneUtils.IsNameGenerated(boneNode.Path))
                    {
                        bone.ClearFlag(BoneFlags.Visible);
                    }
                }

                bones.Add(bone);
            }

            if (ConversionConfig.Current.FixMmdCenterBones)
            {
                // Add master (хЕиуБжуБошжк) and center (уВ╗уГ│уВ┐уГ╝), recompute bone hierarchy.
                PmxBone master = new PmxBone(), center = new PmxBone();

                master.Name        = "хЕиуБжуБошжк";
                master.NameEnglish = "master";
                center.Name        = "уВ╗уГ│уВ┐уГ╝";
                center.NameEnglish = "center";

                master.ParentIndex = 0; // "" bone
                center.ParentIndex = 1; // "master" bone

                master.CurrentPosition = master.InitialPosition = Vector3.Zero;
                center.CurrentPosition = center.InitialPosition = Vector3.Zero;

                master.SetFlag(BoneFlags.Translation | BoneFlags.Rotation);
                center.SetFlag(BoneFlags.Translation | BoneFlags.Rotation);

                bones.Insert(1, master);
                bones.Insert(2, center);

                //// Fix "MODEL_00" bone

                //do {
                //    var model00 = bones.Find(b => b.Name == "уВ░уГлуГ╝уГЦ");

                //    if (model00 == null) {
                //        throw new ArgumentException("MODEL_00 mapped bone is not found.");
                //    }

                //    model00.ParentIndex = 2; // "center" bone
                //} while (false);

                const int numBonesAdded = 2;

                // Fix vertices and other bones
                foreach (var vertex in vertices)
                {
                    foreach (var boneWeight in vertex.BoneWeights)
                    {
                        if (boneWeight.BoneIndex == 0 && boneWeight.Weight <= 0)
                        {
                            continue;
                        }

                        if (boneWeight.BoneIndex >= 1)
                        {
                            boneWeight.BoneIndex += numBonesAdded;
                        }
                    }
                }

                for (var i = numBonesAdded + 1; i < bones.Count; ++i)
                {
                    var bone = bones[i];

                    bone.ParentIndex += numBonesAdded;

                    if (bone.HasFlag(BoneFlags.ToBone))
                    {
                        bone.To_Bone += numBonesAdded;
                    }
                }
            }

            if (ConversionConfig.Current.AppendIKBones)
            {
                // Add IK bones.

                PmxBone[] CreateLegIK(string leftRightJp, string leftRightEn)
                {
                    var startBoneCount = bones.Count;

                    PmxBone ikParent = new PmxBone(), ikBone = new PmxBone();

                    ikParent.Name        = leftRightJp + "ш╢│IKшжк";
                    ikParent.NameEnglish = "leg IKP_" + leftRightEn;
                    ikBone.Name          = leftRightJp + "ш╢│я╝йя╝л";
                    ikBone.NameEnglish   = "leg IK_" + leftRightEn;

                    PmxBone master;

                    do
                    {
                        master = bones.Find(b => b.Name == "хЕиуБжуБошжк");

                        if (master == null)
                        {
                            throw new ArgumentException("Missing master bone.");
                        }
                    } while (false);

                    ikParent.ParentIndex = bones.IndexOf(master);
                    ikBone.ParentIndex   = startBoneCount; // IKP
                    ikParent.SetFlag(BoneFlags.ToBone);
                    ikBone.SetFlag(BoneFlags.ToBone);
                    ikParent.To_Bone = startBoneCount + 1; // IK
                    ikBone.To_Bone   = -1;

                    PmxBone ankle, knee, leg;

                    do
                    {
                        var ankleName = leftRightJp + "ш╢│щжЦ";
                        ankle = bones.Find(b => b.Name == ankleName);
                        var kneeName = leftRightJp + "уБ▓уБЦ";
                        knee = bones.Find(b => b.Name == kneeName);
                        var legName = leftRightJp + "ш╢│";
                        leg = bones.Find(b => b.Name == legName);

                        if (ankle == null)
                        {
                            throw new ArgumentException("Missing ankle bone.");
                        }

                        if (knee == null)
                        {
                            throw new ArgumentException("Missing knee bone.");
                        }

                        if (leg == null)
                        {
                            throw new ArgumentException("Missing leg bone.");
                        }
                    } while (false);

                    ikBone.CurrentPosition   = ikBone.InitialPosition = ankle.InitialPosition;
                    ikParent.CurrentPosition = ikParent.InitialPosition = new Vector3(ikBone.InitialPosition.X, 0, ikBone.InitialPosition.Z);

                    ikParent.SetFlag(BoneFlags.Translation | BoneFlags.Rotation);
                    ikBone.SetFlag(BoneFlags.Translation | BoneFlags.Rotation | BoneFlags.IK);

                    var ik = new PmxIK();

                    ik.LoopCount       = 10;
                    ik.AngleLimit      = MathHelper.DegreesToRadians(114.5916f);
                    ik.TargetBoneIndex = bones.IndexOf(ankle);

                    var links = new IKLink[2];

                    links[0]            = new IKLink();
                    links[0].BoneIndex  = bones.IndexOf(knee);
                    links[0].IsLimited  = true;
                    links[0].LowerBound = new Vector3(MathHelper.DegreesToRadians(-180), 0, 0);
                    links[0].UpperBound = new Vector3(MathHelper.DegreesToRadians(-0.5f), 0, 0);
                    links[1]            = new IKLink();
                    links[1].BoneIndex  = bones.IndexOf(leg);

                    ik.Links  = links;
                    ikBone.IK = ik;

                    return(new[] {
                        ikParent, ikBone
                    });
                }

                PmxBone[] CreateToeIK(string leftRightJp, string leftRightEn)
                {
                    PmxBone ikParent, ikBone = new PmxBone();

                    do
                    {
                        var parentName = leftRightJp + "ш╢│я╝йя╝л";

                        ikParent = bones.Find(b => b.Name == parentName);

                        Debug.Assert(ikParent != null, nameof(ikParent) + " != null");
                    } while (false);

                    ikBone.Name        = leftRightJp + "уБдуБ╛хЕИя╝йя╝л";
                    ikBone.NameEnglish = "toe IK_" + leftRightEn;

                    ikBone.ParentIndex = bones.IndexOf(ikParent);

                    ikBone.SetFlag(BoneFlags.ToBone);
                    ikBone.To_Bone = -1;

                    PmxBone toe, ankle;

                    do
                    {
                        var toeName = leftRightJp + "уБдуБ╛хЕИ";
                        toe = bones.Find(b => b.Name == toeName);
                        var ankleName = leftRightJp + "ш╢│щжЦ";
                        ankle = bones.Find(b => b.Name == ankleName);

                        if (toe == null)
                        {
                            throw new ArgumentException("Missing toe bone.");
                        }

                        if (ankle == null)
                        {
                            throw new ArgumentException("Missing ankle bone.");
                        }
                    } while (false);

                    ikBone.CurrentPosition = ikBone.InitialPosition = toe.InitialPosition;
                    ikBone.SetFlag(BoneFlags.Translation | BoneFlags.Rotation | BoneFlags.IK);

                    var ik = new PmxIK();

                    ik.LoopCount       = 10;
                    ik.AngleLimit      = MathHelper.DegreesToRadians(114.5916f);
                    ik.TargetBoneIndex = bones.IndexOf(toe);

                    var links = new IKLink[1];

                    links[0]           = new IKLink();
                    links[0].BoneIndex = bones.IndexOf(ankle);

                    ik.Links  = links.ToArray();
                    ikBone.IK = ik;

                    return(new[] {
                        ikBone
                    });
                }

                var leftLegIK = CreateLegIK("х╖ж", "L");
                bones.AddRange(leftLegIK);
                var rightLegIK = CreateLegIK("хП│", "R");
                bones.AddRange(rightLegIK);

                var leftToeIK = CreateToeIK("х╖ж", "L");
                bones.AddRange(leftToeIK);
                var rightToeIK = CreateToeIK("хП│", "R");
                bones.AddRange(rightToeIK);
            }

            if (ConversionConfig.Current.AppendEyeBones)
            {
                (int VertexStart1, int VertexCount1, int VertexStart2, int VertexCount2) FindEyesVerticeRange()
                {
                    var meshNameIndex = -1;
                    var cm            = combinedMesh as CompositeMesh;

                    Debug.Assert(cm != null, nameof(cm) + " != null");

                    for (var i = 0; i < cm.Names.Count; i++)
                    {
                        var meshName = cm.Names[i];

                        if (meshName == "eyes")
                        {
                            meshNameIndex = i;
                            break;
                        }
                    }

                    if (meshNameIndex < 0)
                    {
                        throw new ArgumentException("Mesh \"eyes\" is missing.");
                    }

                    var subMeshMaps = cm.ParentMeshIndices.Enumerate().Where(s => s.Value == meshNameIndex).ToArray();

                    Debug.Assert(subMeshMaps.Length == 2, "There should be 2 sub mesh maps.");
                    Debug.Assert(subMeshMaps[1].Index - subMeshMaps[0].Index == 1, "The first sub mesh map should contain one element.");

                    var vertexStart1 = (int)cm.SubMeshes[subMeshMaps[0].Index].FirstVertex;
                    var vertexCount1 = (int)cm.SubMeshes[subMeshMaps[0].Index].VertexCount;
                    var vertexStart2 = (int)cm.SubMeshes[subMeshMaps[1].Index].FirstVertex;
                    var vertexCount2 = (int)cm.SubMeshes[subMeshMaps[1].Index].VertexCount;

                    return(vertexStart1, vertexCount1, vertexStart2, vertexCount2);
                }

                Vector3 GetEyeBonePosition(int vertexStart, int vertexCount)
                {
                    var centerPos = Vector3.Zero;
                    var leftMostPos = new Vector3(float.MinValue, 0, 0);
                    var rightMostPos = new Vector3(float.MaxValue, 0, 0);
                    int leftMostIndex = -1, rightMostIndex = -1;

                    for (var i = vertexStart; i < vertexStart + vertexCount; ++i)
                    {
                        var pos = vertices[i].Position;

                        centerPos += pos;

                        if (pos.X > leftMostPos.X)
                        {
                            leftMostPos   = pos;
                            leftMostIndex = i;
                        }

                        if (pos.X < rightMostPos.X)
                        {
                            rightMostPos   = pos;
                            rightMostIndex = i;
                        }
                    }

                    Debug.Assert(leftMostIndex >= 0, nameof(leftMostIndex) + " >= 0");
                    Debug.Assert(rightMostIndex >= 0, nameof(rightMostIndex) + " >= 0");

                    centerPos = centerPos / vertexCount;

                    // "Eyeball". You got the idea?
                    var leftMostNorm  = vertices[leftMostIndex].Normal;
                    var rightMostNorm = vertices[rightMostIndex].Normal;

                    var   k1 = leftMostNorm.Z / leftMostNorm.X;
                    var   k2 = rightMostNorm.Z / rightMostNorm.X;
                    float x1 = leftMostPos.X, x2 = rightMostPos.X, z1 = leftMostPos.Z, z2 = rightMostPos.Z;

                    var d1 = (z2 - k2 * x2 + k2 * x1 - z1) / (k1 - k2);

                    var x = x1 + d1;
                    var z = z1 + k1 * d1;

                    return(new Vector3(x, centerPos.Y, z));
                }

                Vector3 GetEyesBonePosition(int vertexStart1, int vertexCount1, int vertexStart2, int vertexCount2)
                {
                    var result = new Vector3();

                    for (var i = vertexStart1; i < vertexStart1 + vertexCount1; ++i)
                    {
                        result += vertices[i].Position;
                    }
                    for (var i = vertexStart2; i < vertexStart2 + vertexCount2; ++i)
                    {
                        result += vertices[i].Position;
                    }

                    result = result / (vertexCount1 + vertexCount2);

                    return(new Vector3(0, result.Y + 0.5f, -0.6f));
                }

                var(vs1, vc1, vs2, vc2) = FindEyesVerticeRange();
                PmxBone head;

                do
                {
                    head = bones.Find(b => b.Name == "щан");

                    if (head == null)
                    {
                        throw new ArgumentException("Missing head bone.");
                    }
                } while (false);

                var eyes = new PmxBone();

                eyes.Name        = "ф╕бчЫо";
                eyes.NameEnglish = "eyes";

                eyes.Parent      = head;
                eyes.ParentIndex = bones.IndexOf(head);

                eyes.CurrentPosition = eyes.InitialPosition = GetEyesBonePosition(vs1, vc1, vs2, vc2);

                eyes.SetFlag(BoneFlags.Visible | BoneFlags.Rotation | BoneFlags.ToBone);
                eyes.To_Bone = -1;

                bones.Add(eyes);

                PmxBone leftEye = new PmxBone(), rightEye = new PmxBone();

                leftEye.Name         = "х╖жчЫо";
                leftEye.NameEnglish  = "eye_L";
                rightEye.Name        = "хП│чЫо";
                rightEye.NameEnglish = "eye_R";

                leftEye.Parent       = head;
                leftEye.ParentIndex  = bones.IndexOf(head);
                rightEye.Parent      = head;
                rightEye.ParentIndex = bones.IndexOf(head);

                leftEye.SetFlag(BoneFlags.Visible | BoneFlags.Rotation | BoneFlags.ToBone | BoneFlags.AppendRotation);
                rightEye.SetFlag(BoneFlags.Visible | BoneFlags.Rotation | BoneFlags.ToBone | BoneFlags.AppendRotation);
                leftEye.To_Bone            = -1;
                rightEye.To_Bone           = -1;
                leftEye.AppendParent       = eyes;
                rightEye.AppendParent      = eyes;
                leftEye.AppendParentIndex  = bones.IndexOf(eyes);
                rightEye.AppendParentIndex = bones.IndexOf(eyes);
                leftEye.AppendRatio        = 1;
                rightEye.AppendRatio       = 1;

                leftEye.CurrentPosition  = leftEye.InitialPosition = GetEyeBonePosition(vs1, vc1);
                rightEye.CurrentPosition = rightEye.InitialPosition = GetEyeBonePosition(vs2, vc2);

                bones.Add(leftEye);
                bones.Add(rightEye);

                // Fix vertices
                {
                    var leftEyeIndex  = bones.IndexOf(leftEye);
                    var rightEyeIndex = bones.IndexOf(rightEye);

                    for (var i = vs1; i < vs1 + vc1; ++i)
                    {
                        var skin = vertices[i];
                        // Eyes are only affected by "KUBI/ATAMA" bone by default. So we only need to set one element's values.
                        skin.BoneWeights[0].BoneIndex = leftEyeIndex;
                        Debug.Assert(Math.Abs(skin.BoneWeights[0].Weight - 1) < 0.000001f, "Total weight in the skin of left eye should be 1.");
                    }
                    for (var i = vs2; i < vs2 + vc2; ++i)
                    {
                        var skin = vertices[i];
                        // Eyes are only affected by "KUBI/ATAMA" bone by default. So we only need to set one element's values.
                        skin.BoneWeights[0].BoneIndex = rightEyeIndex;
                        Debug.Assert(Math.Abs(skin.BoneWeights[0].Weight - 1) < 0.000001f, "Total weight in the skin of right eye should be 1.");
                    }
                }
            }

            // Finally, set the indices. The values will be used later.
            for (var i = 0; i < bones.Count; i++)
            {
                bones[i].BoneIndex = i;
            }

            return(bones.ToArray());
        }
            private static void AppendJoints([NotNull, ItemNotNull] IReadOnlyList <PmxBone> bones, [NotNull, ItemNotNull] List <PmxRigidBody> bodies, [NotNull, ItemNotNull] List <PmxJoint> joints)
            {
                foreach (var body in bodies)
                {
                    var addJoint = false;

                    switch (body.Part)
                    {
                    case CoordSystemPart.Torso:
                    case CoordSystemPart.LeftArm:
                    case CoordSystemPart.RightArm:
                    case CoordSystemPart.Legs:
                    case CoordSystemPart.Head:
                        break;

                    case CoordSystemPart.Skirt:
                    case CoordSystemPart.Hair:
                    case CoordSystemPart.Breasts:
                        addJoint = true;
                        break;

                    case CoordSystemPart.Accessories:
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    if (!addJoint)
                    {
                        continue;
                    }

                    var pmxBone    = bones[body.BoneIndex];
                    var parentBone = bones[pmxBone.ParentIndex];

                    var parentBody = bodies.Find(bo => bo.BoneIndex == parentBone.BoneIndex);

                    // This rigid body is attached to a semi-root bone. Create it as a new "root" for its sub rigid bodies.
                    if (parentBody == null)
                    {
                        switch (body.Part)
                        {
                        case CoordSystemPart.Skirt:
                            var koshi = BoneUtils.GetPmxBoneName("MODEL_00/BASE/KOSHI");
                            parentBody = bodies.FirstOrDefault(bo => bones[bo.BoneIndex].Name == koshi);
                            break;

                        case CoordSystemPart.Hair:
                            // The first one is the one on body (which is larger).
                            // The second one is what should be applied to the hair.
                            parentBody = bodies.Where(bo => bones[bo.BoneIndex].Name == "ATAMA").Skip(1).FirstOrDefault();
                            break;

                        case CoordSystemPart.Breasts:
                            var mune2 = BoneUtils.GetPmxBoneName("MODEL_00/BASE/MUNE1/MUNE2");
                            parentBody = bodies.FirstOrDefault(bo => bones[bo.BoneIndex].Name == mune2);
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }
                    }

                    var joint = new PmxJoint();

                    joint.BodyIndex1 = bodies.IndexOf(parentBody);
                    joint.BodyIndex2 = bodies.IndexOf(body);

                    joint.Name        = body.Name;
                    joint.NameEnglish = body.NameEnglish;

                    switch (body.Part)
                    {
                    case CoordSystemPart.Hair:
                    case CoordSystemPart.Skirt:
                        joint.Kind = JointKind.Spring6Dof;
                        break;

                    default:
                        joint.Kind = JointKind.Spring6Dof;
                        break;
                    }

                    joint.Position = pmxBone.InitialPosition;

                    // Now calculate rotation for this joint.
                    var childBones = bones.Where(b => b.ParentIndex == pmxBone.BoneIndex).ToArray();

                    if (childBones.Length > 1)
                    {
                        Debug.Print("Warning: more than one child bone for joint rotation calculation. Aborting.");
                        continue;
                    }

                    PmxBone rotBone1, rotBone2;

                    if (childBones.Length == 0)
                    {
                        rotBone1 = parentBone;
                        rotBone2 = pmxBone;
                    }
                    else
                    {
                        rotBone1 = pmxBone;
                        rotBone2 = childBones[0];
                    }

                    var delta = rotBone2.InitialPosition - rotBone1.InitialPosition;

                    var qy = (float)Math.Atan2(delta.X, delta.Z);
                    var qx = (float)Math.Atan2(delta.Y, delta.Z);
                    //const float qx = 0;
                    //var qz = (float)Math.Atan2(delta.X, delta.Y);
                    const float qz = 0;

                    joint.Rotation = new Vector3(qx, qy, qz);

                    if (joint != null)
                    {
                        joints.Add(joint);
                    }
                }
            }
Example #12
0
        protected void ParseActorProperty(Node node, string name)
        {
            switch (name)
            {
            case "Name":
                node.Id = lexer.ParseQuotedString();
                break;

            case "RuntimeClass":
                lexer.ParseQuotedString();
                break;

            case "Localizable":
                lexer.ParseBool();
                break;

            case "Source":
                var s = lexer.ParsePath();
                node.ContentsPath = s;
                break;

            case "Attributes":
                node.TangerineFlags = (TangerineFlags)(lexer.ParseInt() & 7);
                break;

            case "Trigger":
                node.Trigger = lexer.ParseQuotedString();
                break;

            case "Tag":
                node.Tag = lexer.ParseQuotedString();
                break;

            case "Actors":
                lexer.ParseToken('[');
                while (lexer.PeekChar() != ']')
                {
                    var child = ParseNode(null);
                    if (child != null)
                    {
                        node.Nodes.Add(child);
                    }
                }
                lexer.ParseToken(']');
                if (node is Widget)
                {
                    var allBones = node.AsWidget.Nodes.OfType <Bone>();
                    foreach (var root in allBones.Where(b => b.BaseIndex == 0))
                    {
                        var loc = node.AsWidget.Nodes.IndexOf(root);
                        if (loc == 0)
                        {
                            loc++;
                        }
                        var bones = BoneUtils.SortBones(BoneUtils.FindBoneDescendats(root, allBones));
                        foreach (var bone in bones)
                        {
                            bone.Unlink();
                            node.AsWidget.Nodes.Insert(loc, bone);
                        }
                    }
                }
                break;

            case "Animators":
                lexer.ParseToken('[');
                while (lexer.PeekChar() != ']')
                {
                    ParseAnimator(node);
                }
                if (node is ParticleModifier)
                {
                    TryMergeScaleAndAspectRatioForParticleTemplate(node as ParticleModifier);
                    particleModifierScaleAnimator       = null;
                    particleModifierAspectRatioAnimator = null;
                }
                lexer.ParseToken(']');
                break;

            case "Markers":
                lexer.ParseToken('[');
                while (lexer.PeekChar() != ']')
                {
                    var marker = ParseMarker();
                    if (marker.Action == MarkerAction.Jump && marker.JumpTo == null)
                    {
                        throw new Exception("Jump marker '{0}' in node '{1}' have no JumpTo property.", marker.Id ?? "<noname>", node.ToString());
                    }
                    node.Markers.Add(marker);
                }
                lexer.ParseToken(']');
                break;

            default:
                throw new Exception("Unknown property '{0}'. Parsing: {1}", name, node.GetType());
            }
        }
Example #13
0
        public static bool PasteNodes(string data, RowLocation location, Vector2?mousePosition)
        {
            bool CanPaste()
            {
                // We are support only paste into folders for now.
                return(location.ParentRow.Components.Contains <FolderRow>() ||
                       location.ParentRow.Components.Contains <BoneRow>());
            }

            if (!CanPaste())
            {
                return(false);
            }
            Frame frame;

            try {
                var stream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(data));
                frame = TangerinePersistence.Instance.ReadObject <Frame>(Document.Current.Path, stream);
            } catch (System.Exception e) {
                Debug.Write(e);
                return(false);
            }
            var animators = frame.Animators;
            var items     = frame.RootFolder().Items.Where(item => NodeCompositionValidator.IsCopyPasteAllowed(item.GetType())).ToList();

            if (items.Count == 0)
            {
                if (animators.Count != 0)
                {
                    foreach (var row in Document.Current.TopLevelSelectedRows().ToList())
                    {
                        if (!(row.Components.Get <NodeRow>()?.Node is IAnimationHost animable))
                        {
                            continue;
                        }
                        Document.Current.History.DoTransaction(() => {
                            foreach (var animator in animators)
                            {
                                if (animable.GetType().GetProperty(animator.TargetPropertyPath) == null)
                                {
                                    continue;
                                }
                                foreach (var keyframe in animator.Keys)
                                {
                                    SetKeyframe.Perform(animable, animator.TargetPropertyPath, animator.AnimationId, keyframe);
                                }
                            }
                        });
                    }
                }
                return(true);
            }
            var folderLocation = location.ParentRow.Rows.Count > 0 ?
                                 Row.GetFolderItemLocation(location.ParentRow.Rows[location.Index]) :
                                 new FolderItemLocation {
                Index = 0, Folder = location.ParentRow.Components.Get <FolderRow>().Folder
            };

            if (!folderLocation.Folder.Expanded)
            {
                SetProperty.Perform(folderLocation.Folder, nameof(Folder.Expanded), true);
            }
            mousePosition *= Document.Current.Container.AsWidget?.LocalToWorldTransform.CalcInversed();
            var shift = mousePosition - items.OfType <Widget>().FirstOrDefault()?.Position;

            foreach (var n in items.OfType <Node>())
            {
                Document.Current.Decorate(n);
            }
            if (shift.HasValue)
            {
                foreach (var w in items.OfType <Widget>())
                {
                    w.Position += shift.Value;
                }
            }
            frame.RootFolder().Items.Clear();
            frame.RootFolder().SyncDescriptorsAndNodes(frame);
            ClearRowSelection.Perform();
            while (items.Count > 0)
            {
                var item = items.First();
                if (item is Bone bone)
                {
                    if (bone.BaseIndex != 0)
                    {
                        continue;
                    }
                    var newIndex = 1;
                    var bones    = Document.Current.Container.Nodes.OfType <Bone>();
                    if (bones.Any())
                    {
                        newIndex = bones.Max(b => b.Index) + 1;
                    }
                    var children = BoneUtils.FindBoneDescendats(bone, items.OfType <Bone>()).ToList();
                    var map      = new Dictionary <int, int> {
                        { bone.Index, newIndex }
                    };
                    bone.BaseIndex = location.ParentRow.Components.Get <BoneRow>()?.Bone.Index ?? 0;
                    bone.Index     = newIndex;
                    InsertFolderItem.Perform(
                        Document.Current.Container,
                        folderLocation, bone);
                    folderLocation.Index++;
                    foreach (var b in children)
                    {
                        b.BaseIndex = map[b.BaseIndex];
                        map.Add(b.Index, b.Index = ++newIndex);
                        InsertFolderItem.Perform(
                            Document.Current.Container,
                            folderLocation, b);
                        folderLocation.Index++;
                        items.Remove(b);
                    }
                    Document.Current.Container.RootFolder().SyncDescriptorsAndNodes(Document.Current.Container);
                    SortBonesInChain.Perform(bone);
                    SelectRow.Perform(Document.Current.GetRowForObject(item));
                }
                else
                {
                    if (!location.ParentRow.Components.Contains <BoneRow>())
                    {
                        InsertFolderItem.Perform(
                            Document.Current.Container,
                            folderLocation, item);
                        folderLocation.Index++;
                        SelectRow.Perform(Document.Current.GetRowForObject(item));
                    }
                }
                items.Remove(item);
            }
            return(true);
        }
            private static void AppendStaticBodies([NotNull, ItemNotNull] IReadOnlyList <PmxBone> bones, [NotNull, ItemNotNull] IReadOnlyList <SwayCollider> swayColliders, [NotNull, ItemNotNull] List <PmxRigidBody> bodies)
            {
                foreach (var collider in swayColliders)
                {
                    var mltdBoneName = collider.Path;

                    if (mltdBoneName.Contains("BODY_SCALE/"))
                    {
                        mltdBoneName = mltdBoneName.Replace("BODY_SCALE/", string.Empty);
                    }

                    var pmxBoneName = BoneUtils.GetPmxBoneName(mltdBoneName);

                    var body = new PmxRigidBody();
                    var correspondingBone = bones.FirstOrDefault(o => o.Name == pmxBoneName);

                    Debug.Assert(correspondingBone != null, nameof(correspondingBone) + " != null");

                    body.Name        = correspondingBone.Name;
                    body.NameEnglish = correspondingBone.NameEnglish;

                    body.BoneIndex = correspondingBone.BoneIndex;

                    body.KineticMode = KineticMode.Static;

                    body.Mass            = 1;
                    body.PositionDamping = 0.9f;
                    body.RotationDamping = 1.0f;
                    body.Friction        = 0.0f;
                    body.Restitution     = 0.0f;

                    var part = GetBodyCoordSystemPart(collider);

                    body.Part = part;

                    switch (part)
                    {
                    case CoordSystemPart.Torso:
                        body.GroupIndex = PassGroupIndex.Torso;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        body.PassGroup.Pass(PassGroupIndex.Arms);
                        body.PassGroup.Pass(PassGroupIndex.Legs);
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        break;

                    case CoordSystemPart.LeftArm:
                    case CoordSystemPart.RightArm:
                        body.GroupIndex = PassGroupIndex.Arms;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        body.PassGroup.Pass(PassGroupIndex.Arms);
                        body.PassGroup.Pass(PassGroupIndex.Legs);
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        break;

                    case CoordSystemPart.Legs:
                        body.GroupIndex = PassGroupIndex.Legs;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        body.PassGroup.Pass(PassGroupIndex.Arms);
                        body.PassGroup.Pass(PassGroupIndex.Legs);
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        break;

                    case CoordSystemPart.Head:
                        body.GroupIndex = PassGroupIndex.Head;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        body.PassGroup.Pass(PassGroupIndex.Arms);
                        body.PassGroup.Pass(PassGroupIndex.Legs);
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    switch (collider.Type)
                    {
                    case ColliderType.Sphere: {
                        body.BoundingBoxKind = BoundingBoxKind.Sphere;
                        body.BoundingBoxSize = new Vector3(collider.Radius, 0, 0);
                        body.Position        = correspondingBone.InitialPosition;
                    }
                    break;

                    case ColliderType.Capsule: {
                        body.BoundingBoxKind = BoundingBoxKind.Capsule;
                        body.BoundingBoxSize = new Vector3(collider.Radius, collider.Distance, 0);
                        body.RotationAngles  = MapRotation(part, collider.Axis);

                        var offset = MapTranslation(collider.Offset, part, collider.Axis);

                        if (ConversionConfig.Current.ScaleToPmxSize)
                        {
                            offset = offset * ScalingConfig.ScaleUnityToPmx;
                        }

                        body.Position = correspondingBone.InitialPosition + offset;
                    }
                    break;

                    case ColliderType.Plane:
                    case ColliderType.Bridge:
                        // TODO: How to handle these?
                        continue;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    if (ConversionConfig.Current.ScaleToPmxSize)
                    {
                        body.BoundingBoxSize = body.BoundingBoxSize * ScalingConfig.ScaleUnityToPmx;
                    }

                    bodies.Add(body);
                }
            }
Example #15
0
        public override void ExecuteTransaction()
        {
            var selectedNodes = Document.Current.SelectedNodes().Where(IsValidNode).ToList();

            if (!Utils.CalcHullAndPivot(selectedNodes, out var hull, out _))
            {
                return;
            }

            hull = hull.Transform(Document.Current.Container.AsWidget.LocalToWorldTransform.CalcInversed());
            var aabb      = hull.ToAABB();
            var container = Document.Current.Container;

            foreach (var row in Document.Current.SelectedRows())
            {
                if (row.Components.Contains <BoneRow>())
                {
                    var boneRow = row.Components.Get <BoneRow>();
                    if (!boneRow.ChildrenExpanded)
                    {
                        selectedNodes.AddRange(BoneUtils.FindBoneDescendats(boneRow.Bone, container.Nodes.OfType <Bone>()));
                    }
                }
            }
            var selectedBones = selectedNodes.OfType <Bone>().ToList();

            var   loc = container.RootFolder().Find(selectedNodes[0]);
            Frame group;

            try {
                group = (Frame)Core.Operations.CreateNode.Perform(container, loc, typeof(Frame));
            } catch (InvalidOperationException e) {
                AlertDialog.Show(e.Message);
                return;
            }
            group.Id       = selectedNodes[0].Id + "Group";
            group.Pivot    = Vector2.Half;
            group.Position = aabb.Center;
            group.Size     = aabb.Size;
            var bonesExceptSelected = container.Nodes.Except(selectedNodes).OfType <Bone>().ToList();

            UntieWidgetsFromBones.Perform(bonesExceptSelected, selectedNodes.OfType <Widget>());
            UntieWidgetsFromBones.Perform(selectedBones, container.Nodes.Except(selectedNodes).OfType <Widget>());
            var nodeKeyframesDict = new Dictionary <Node, BoneAnimationData>();
            var localRoots        = new List <Bone>();

            foreach (var bone in BoneUtils.SortBones(container.Nodes.OfType <Bone>()))
            {
                Bone localRoot;
                var  delta          = Vector2.Zero;
                var  isSelectedBone = selectedBones.Contains(bone);
                if (isSelectedBone)
                {
                    localRoot = BoneUtils.FindBoneRoot(bone, selectedNodes);
                    delta     = -aabb.A;
                }
                else
                {
                    localRoot = BoneUtils.FindBoneRoot(bone, bonesExceptSelected);
                }
                if (!localRoots.Contains(localRoot))
                {
                    if (!isSelectedBone && localRoot.BaseIndex == 0)
                    {
                        localRoots.Add(localRoot);
                        continue;
                    }
                    nodeKeyframesDict.Add(localRoot, EvaluateBoneAnimationUsingParent(localRoot, v => v + delta));
                    localRoots.Add(localRoot);
                }
            }
            SetKeyframes(nodeKeyframesDict);
            foreach (var n in selectedNodes)
            {
                UnlinkFolderItem.Perform(container, n);
            }
            int i = 0;

            foreach (var node in selectedNodes)
            {
                InsertFolderItem.Perform(group, new FolderItemLocation(group.RootFolder(), i++), node);
                if (node is Widget)
                {
                    TransformPropertyAndKeyframes <Vector2>(node, nameof(Widget.Position), v => v - aabb.A);
                }
                if (node is Bone)
                {
                    TransformPropertyAndKeyframes <Vector2>(node, nameof(Bone.RefPosition), v => v - aabb.A);
                }
            }
            group.DefaultAnimation.Frame = container.DefaultAnimation.Frame;
            ClearRowSelection.Perform();
            SelectNode.Perform(group);
        }
        private static IReadOnlyList <VmdBoneFrame> CreateBoneFrames([NotNull] CharacterImasMotionAsset bodyMotion, [NotNull] Avatar avatar, [NotNull] PmxModel pmx)
        {
            var mltdHierarchy = BoneUtils.BuildBoneHierarchy(avatar);
            var pmxHierarchy  = BoneUtils.BuildBoneHierarchy(pmx);

            if (ConversionConfig.Current.AppendIKBones || ConversionConfig.Current.AppendEyeBones)
            {
                throw new NotSupportedException("Character motion frames generation (from MLTD) is not supported when appending bones (eyes and/or IK) is enabled.");
            }
            else
            {
                Debug.Assert(mltdHierarchy.Count == pmxHierarchy.Count, "Hierarchy number should be equal between MLTD and MMD.");
            }

            foreach (var mltdBone in mltdHierarchy)
            {
                mltdBone.Initialize();
            }

            foreach (var pmxBone in pmxHierarchy)
            {
                pmxBone.Initialize();
            }

            var animation         = BodyAnimation.CreateFrom(bodyMotion);
            var boneCount         = mltdHierarchy.Count;
            var animatedBoneCount = animation.BoneCount;
            var keyFrameCount     = animation.KeyFrames.Count;

            do
            {
                void MarkNamedBone(string name)
                {
                    var bone = pmx.Bones.FirstOrDefault(b => b.Name == name);

                    if (bone != null)
                    {
                        bone.IsMltdKeyBone = true;
                    }
                    else
                    {
                        Debug.Print("Warning: trying to mark bone {0} as MLTD key bone but the bone is missing from the model.", name);
                    }
                }

                var names1 = animation.KeyFrames.Take(animatedBoneCount)
                             .Select(kf => kf.Path).ToArray();
                var names = names1.Select(BoneUtils.GetVmdBoneNameFromBonePath).ToArray();
                // Mark MLTD key bones.
                foreach (var name in names)
                {
                    MarkNamedBone(name);
                }

                // Special cases
                MarkNamedBone("KUBI");
                MarkNamedBone("щан");
            } while (false);

            Debug.Assert(keyFrameCount % animatedBoneCount == 0, "keyFrameCount % animatedBoneCount == 0");

            var iterationTimes = keyFrameCount / animatedBoneCount;
            var boneFrameList  = new List <VmdBoneFrame>();

            for (var i = 0; i < iterationTimes; ++i)
            {
                if (ConversionConfig.Current.Transform60FpsTo30Fps)
                {
                    if (i % 2 == 1)
                    {
                        continue;
                    }
                }

                var keyFrameIndexStart = i * animatedBoneCount;

                for (var j = 0; j < animatedBoneCount; ++j)
                {
                    var keyFrame     = animation.KeyFrames[keyFrameIndexStart + j];
                    var mltdBoneName = keyFrame.Path.Replace("BODY_SCALE/", string.Empty);
                    var targetBone   = mltdHierarchy.SingleOrDefault(bone => bone.Name == mltdBoneName);

                    if (targetBone == null)
                    {
                        //throw new ArgumentException("Bone not found.");
                        continue; // Shika doesn't have the "POSITION" bone.
                    }

                    BoneNode transferredBone = null;

                    foreach (var kv in BoneAttachmentMap)
                    {
                        if (kv.Key == mltdBoneName)
                        {
                            transferredBone = mltdHierarchy.SingleOrDefault(bone => bone.Name == kv.Value);

                            if (transferredBone == null)
                            {
                                throw new ArgumentException();
                            }

                            break;
                        }
                    }

                    if (keyFrame.HasPositions)
                    {
                        var x = keyFrame.PositionX.Value;
                        var y = keyFrame.PositionY.Value;
                        var z = keyFrame.PositionZ.Value;

                        var t = new Vector3(x, y, z);

                        t = t.FixUnityToOpenTK();

                        if (ConversionConfig.Current.ScaleToVmdSize)
                        {
                            t = t * ScalingConfig.ScaleUnityToPmx;
                        }

                        targetBone.LocalPosition = t;

                        //if (transferredBone != null) {
                        //    transferredBone.LocalPosition = t;
                        //}
                    }

                    if (keyFrame.HasRotations)
                    {
                        var x = keyFrame.AngleX.Value;
                        var y = keyFrame.AngleY.Value;
                        var z = keyFrame.AngleZ.Value;

                        var q = UnityRotation.EulerDeg(x, y, z);

                        q = q.FixUnityToOpenTK();

                        targetBone.LocalRotation = q;

                        if (transferredBone != null)
                        {
                            transferredBone.LocalRotation = q;
                        }
                    }
                }

                foreach (var mltdBone in mltdHierarchy)
                {
                    mltdBone.UpdateTransform();
                }

                for (var j = 0; j < boneCount; ++j)
                {
                    var pmxBone  = pmxHierarchy[j];
                    var mltdBone = mltdHierarchy[j];

                    {
                        var pb = pmx.Bones.FirstOrDefault(b => b.Name == pmxBone.Name);

                        Debug.Assert(pb != null, $"PMX bone with the name \"{pmxBone.Name}\" should exist.");

                        if (!pb.IsMltdKeyBone)
                        {
                            continue;
                        }
                    }

                    var skinMatrix      = mltdBone.SkinMatrix;
                    var mPmxBindingPose = pmxBone.BindingPose;
                    var mWorld          = pmxBone.Parent?.WorldMatrix ?? Matrix4.Identity;

                    // skinMatrix == inv(mPmxBindingPose) x mLocal x mWorld
                    var mLocal = mPmxBindingPose * skinMatrix * mWorld.Inverted();

                    // Here, translation is in... world coords? WTF?
                    var t = mLocal.ExtractTranslation();
                    var q = mLocal.ExtractRotation();

                    if (pmxBone.Parent != null)
                    {
                        t = t - (pmxBone.InitialPosition - pmxBone.Parent.InitialPosition);
                    }

                    int frameIndex;

                    if (ConversionConfig.Current.Transform60FpsTo30Fps)
                    {
                        frameIndex = i / 2;
                    }
                    else
                    {
                        frameIndex = i;
                    }

                    var vmdBoneName = BoneUtils.GetVmdBoneNameFromBoneName(mltdBone.Path);
                    var boneFrame   = new VmdBoneFrame(frameIndex, vmdBoneName);

                    boneFrame.Position = t;
                    boneFrame.Rotation = q;

                    boneFrameList.Add(boneFrame);

                    pmxBone.LocalPosition = t;
                    pmxBone.LocalRotation = q;
                    pmxBone.UpdateTransform();
                }
            }

            return(boneFrameList);
        }
Example #17
0
        public override void ExecuteTransaction()
        {
            var groups = Document.Current?.SelectedNodes().OfType <Frame>().ToList();

            if (groups?.Count == 0)
            {
                return;
            }
            var container = (Widget)Document.Current.Container;
            var p         = container.RootFolder().Find(groups[0]);

            ClearRowSelection.Perform();
            UntieWidgetsFromBones.Perform(Document.Current.Container.Nodes.OfType <Bone>(), groups);
            foreach (var group in groups)
            {
                UnlinkFolderItem.Perform(container, group);
            }

            foreach (var group in groups)
            {
                var flipXFactor            = group.Scale.X < 0 ? -1 : 1;
                var flipYFactor            = group.Scale.Y < 0 ? -1 : 1;
                var flipVector             = Vector2.Right + Vector2.Down * flipXFactor * flipYFactor;
                var groupRootBones         = new List <Bone>();
                var groupNodes             = group.Nodes.ToList().Where(GroupNodes.IsValidNode).ToList();
                var localToParentTransform = group.CalcLocalToParentTransform();
                foreach (var node in groupNodes)
                {
                    UnlinkFolderItem.Perform(group, node);
                    InsertFolderItem.Perform(container, p, node);
                    SelectNode.Perform(node);
                    p.Index++;
                    if (node is Widget)
                    {
                        GroupNodes.TransformPropertyAndKeyframes <Vector2>(node, nameof(Widget.Position), v => localToParentTransform * v);
                        GroupNodes.TransformPropertyAndKeyframes <Vector2>(node, nameof(Widget.Scale), v => v * group.Scale);
                        GroupNodes.TransformPropertyAndKeyframes <float>(node, nameof(Widget.Rotation),
                                                                         v => v * Mathf.Sign(group.Scale.X * group.Scale.Y) + group.Rotation);
                        GroupNodes.TransformPropertyAndKeyframes <Color4>(node, nameof(Widget.Color), v => group.Color * v);
                    }
                    else if (node is Bone)
                    {
                        var root = BoneUtils.FindBoneRoot((Bone)node, groupNodes);
                        if (!groupRootBones.Contains(root))
                        {
                            GroupNodes.TransformPropertyAndKeyframes <Vector2>(node, nameof(Bone.Position), v => localToParentTransform * v);
                            GroupNodes.TransformPropertyAndKeyframes <float>(node, nameof(Bone.Rotation),
                                                                             v => (Matrix32.Rotation(v * Mathf.DegToRad) * localToParentTransform).ToTransform2().Rotation);
                            groupRootBones.Add(root);
                        }
                        else if (flipVector != Vector2.One)
                        {
                            GroupNodes.TransformPropertyAndKeyframes <Vector2>(node, nameof(Bone.Position), v => v * flipVector);
                            GroupNodes.TransformPropertyAndKeyframes <float>(node, nameof(Bone.Rotation), v => - v);
                        }
                        GroupNodes.TransformPropertyAndKeyframes <Vector2>(node, nameof(Bone.RefPosition), v => localToParentTransform * v);
                        GroupNodes.TransformPropertyAndKeyframes <float>(node, nameof(Bone.RefRotation),
                                                                         v => (Matrix32.Rotation(v * Mathf.DegToRad) * localToParentTransform).ToTransform2().Rotation);
                    }
                }
            }
        }
            private static void AppendSwayBones([NotNull, ItemNotNull] IReadOnlyList <PmxBone> bones, [NotNull, ItemNotNull] List <SwayBone> swayBones, [NotNull, ItemNotNull] List <PmxRigidBody> bodies)
            {
                // "Semi-root" sway bones
                foreach (var swayBone in swayBones)
                {
                    var part = GetBodyCoordSystemPart(swayBone);

                    switch (part)
                    {
                    case CoordSystemPart.Torso:
                    case CoordSystemPart.LeftArm:
                    case CoordSystemPart.RightArm:
                    case CoordSystemPart.Legs:
                    case CoordSystemPart.Head:
                        continue;

                    case CoordSystemPart.Breasts:
                    case CoordSystemPart.Accessories:
                        continue;

                    case CoordSystemPart.Skirt:
                    case CoordSystemPart.Hair:
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    var guessedParentPath = swayBone.Path.BreakFirst('/');
                    var parentBody        = swayBones.Find(sb => sb.Path == guessedParentPath);

                    if (parentBody != null)
                    {
                        // This is a child rigid body.
                        continue;
                    }

                    var mltdBoneName = swayBone.Path;

                    if (mltdBoneName.Contains("BODY_SCALE/"))
                    {
                        mltdBoneName = mltdBoneName.Replace("BODY_SCALE/", string.Empty);
                    }

                    var pmxBoneName = BoneUtils.GetPmxBoneName(mltdBoneName);

                    var body = new PmxRigidBody();

                    body.Part = part;

                    var correspondingBone = bones.FirstOrDefault(o => o.Name == pmxBoneName);

                    Debug.Assert(correspondingBone != null, "Semi-root sway: " + nameof(correspondingBone) + " != null");

                    body.BoneIndex = correspondingBone.BoneIndex;

                    body.Name        = correspondingBone.Name;
                    body.NameEnglish = correspondingBone.NameEnglish;

                    body.KineticMode = KineticMode.Dynamic;

                    body.Mass = ComputeMass(swayBone.Radius);

                    body.PositionDamping = 0.9f;
                    body.RotationDamping = 1.0f;
                    body.Friction        = 0.0f;
                    body.Restitution     = 0.0f;

                    switch (part)
                    {
                    case CoordSystemPart.Head:
                        body.GroupIndex = PassGroupIndex.Head;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        body.PassGroup.Pass(PassGroupIndex.Arms);
                        body.PassGroup.Pass(PassGroupIndex.Legs);
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        break;

                    case CoordSystemPart.Hair:
                        body.GroupIndex = PassGroupIndex.Hair;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        body.PassGroup.Pass(PassGroupIndex.Hair);
                        break;

                    case CoordSystemPart.Skirt:
                        body.GroupIndex = PassGroupIndex.Skirt;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Skirt);
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        break;

                    case CoordSystemPart.Breasts:
                        body.GroupIndex = PassGroupIndex.Breasts;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Breasts);
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        break;

                    case CoordSystemPart.Accessories:
                        body.GroupIndex = PassGroupIndex.Accessories;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Accessories);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    switch (swayBone.Type)
                    {
                    case ColliderType.Sphere: {
                        body.BoundingBoxKind = BoundingBoxKind.Sphere;
                        body.BoundingBoxSize = new Vector3(swayBone.Radius, 0, 0);

                        var childBone = bones.FirstOrDefault(bo => bo.ParentIndex == correspondingBone.BoneIndex);

                        Debug.Assert(childBone != null, nameof(childBone) + " != null");

                        body.Position = (correspondingBone.InitialPosition + childBone.InitialPosition) / 2;
                    }
                    break;

                    case ColliderType.Capsule:
                    case ColliderType.Plane:
                    case ColliderType.Bridge:
                        throw new ArgumentOutOfRangeException(nameof(swayBone.Type), swayBone.Type, "Only sphere colliders are supported in SwayBone.");

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    if (ConversionConfig.Current.ScaleToPmxSize)
                    {
                        body.BoundingBoxSize = body.BoundingBoxSize * ScalingConfig.ScaleUnityToPmx;
                    }

                    bodies.Add(body);
                }

                // Normal sway bones
                foreach (var swayBone in swayBones)
                {
                    var mltdBoneName = swayBone.Path;

                    if (mltdBoneName.Contains("BODY_SCALE/"))
                    {
                        mltdBoneName = mltdBoneName.Replace("BODY_SCALE/", string.Empty);
                    }

                    var pmxBoneName = BoneUtils.GetPmxBoneName(mltdBoneName);

                    var body = new PmxRigidBody();
                    var part = GetBodyCoordSystemPart(swayBone);

                    body.Part = part;

                    var correspondingBone = bones.FirstOrDefault(o => o.Name == pmxBoneName);

                    Debug.Assert(correspondingBone != null, "Normal sway: " + nameof(correspondingBone) + " != null");

                    switch (part)
                    {
                    case CoordSystemPart.Skirt:
                    case CoordSystemPart.Hair:
                        correspondingBone = bones.FirstOrDefault(b => b.ParentIndex == correspondingBone.BoneIndex);
                        Debug.Assert(correspondingBone != null, "Normal sway: " + nameof(correspondingBone) + " != null");
                        break;

                    case CoordSystemPart.Accessories:
                    case CoordSystemPart.Breasts:
                        break;
                    }

                    body.BoneIndex = correspondingBone.BoneIndex;

                    body.Name        = correspondingBone.Name;
                    body.NameEnglish = correspondingBone.NameEnglish;

                    switch (part)
                    {
                    case CoordSystemPart.Skirt:
                    case CoordSystemPart.Hair:
                        body.KineticMode = KineticMode.Dynamic;
                        break;

                    case CoordSystemPart.Accessories:
                    case CoordSystemPart.Breasts:
                        body.KineticMode = KineticMode.DynamicWithBone;
                        break;
                    }

                    body.Mass = ComputeMass(swayBone.Radius);

                    body.PositionDamping = 0.9f;
                    body.RotationDamping = 1.0f;
                    body.Friction        = 0.0f;
                    body.Restitution     = 0.0f;

                    switch (part)
                    {
                    case CoordSystemPart.Head:
                        body.GroupIndex = PassGroupIndex.Head;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        body.PassGroup.Pass(PassGroupIndex.Arms);
                        body.PassGroup.Pass(PassGroupIndex.Legs);
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        break;

                    case CoordSystemPart.Hair:
                        body.GroupIndex = PassGroupIndex.Hair;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Hair);
                        body.PassGroup.Pass(PassGroupIndex.Head);
                        break;

                    case CoordSystemPart.Skirt:
                        body.GroupIndex = PassGroupIndex.Skirt;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Skirt);
                        break;

                    case CoordSystemPart.Breasts:
                        body.GroupIndex = PassGroupIndex.Breasts;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Breasts);
                        body.PassGroup.Pass(PassGroupIndex.Torso);
                        break;

                    case CoordSystemPart.Accessories:
                        body.GroupIndex = PassGroupIndex.Accessories;
                        body.PassGroup.BlockAll();
                        body.PassGroup.Pass(PassGroupIndex.Accessories);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    switch (swayBone.Type)
                    {
                    case ColliderType.Sphere: {
                        body.BoundingBoxKind = BoundingBoxKind.Sphere;
                        body.BoundingBoxSize = new Vector3(swayBone.Radius, 0, 0);

                        var childBone = bones.FirstOrDefault(bo => bo.ParentIndex == correspondingBone.BoneIndex);

                        if (childBone != null)
                        {
                            body.Position = (correspondingBone.InitialPosition + childBone.InitialPosition) / 2;
                        }
                        else
                        {
                            var parentBone = bones.FirstOrDefault(bo => bo.BoneIndex == correspondingBone.ParentIndex);

                            Debug.Assert(parentBone != null, nameof(parentBone) + " != null");

                            var delta = correspondingBone.InitialPosition - parentBone.InitialPosition;

                            body.Position = correspondingBone.InitialPosition + delta / 2;
                        }
                    }
                    break;

                    case ColliderType.Capsule:
                    case ColliderType.Plane:
                    case ColliderType.Bridge:
                        throw new ArgumentOutOfRangeException(nameof(swayBone.Type), swayBone.Type, "Only sphere colliders are supported in SwayBone.");

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    if (ConversionConfig.Current.ScaleToPmxSize)
                    {
                        body.BoundingBoxSize = body.BoundingBoxSize * ScalingConfig.ScaleUnityToPmx;
                    }

                    bodies.Add(body);
                }
            }