Exemple #1
0
        private BoneNode[] BuildBoneHierarchy([NotNull] PrettyAvatar avatar, bool fixKubi)
        {
            var boneList        = new List <BoneNode>();
            var skeletonNodes   = avatar.AvatarSkeleton.Nodes;
            var scaleUnityToPmx = _scalingConfig.ScaleUnityToPmx;

            for (var i = 0; i < skeletonNodes.Length; i++)
            {
                var n = skeletonNodes[i];

                var parent   = n.ParentIndex >= 0 ? boneList[n.ParentIndex] : null;
                var boneId   = avatar.AvatarSkeleton.NodeIDs[i];
                var bonePath = avatar.BoneNamesMap[boneId];

                var boneIndex = avatar.AvatarSkeleton.NodeIDs.FindIndex(boneId);

                if (boneIndex < 0)
                {
                    throw new IndexOutOfRangeException();
                }

                var initialPose = avatar.AvatarSkeletonPose.Transforms[boneIndex];

                var t = initialPose.LocalPosition.ToOpenTK() * scaleUnityToPmx;
                var q = initialPose.LocalRotation.ToOpenTK();

                var bone = new BoneNode(parent, i, bonePath, t, q);

                boneList.Add(bone);
            }

            PostprocessBoneList(boneList, fixKubi, false);

            return(boneList.ToArray());
        }
        private static Domain.Nodes.Node BuildBoneNode(DocumentData data, Node node)
        {
            var domainNode = new BoneNode(data.MessageBus, node.Id, node.Name)
            {
                Parent = node.ParentId == 0
                    ? null
                    : data.Nodes[node.ParentId] // node list is in hierarchical order, so parents always are available when their children are added.
            };

            if (domainNode.Parent == null)
            {
                data.RootNodes.InternalAdd(domainNode);
            }
            else
            {
                domainNode.Parent?.InternalAdd(domainNode);
            }

            domainNode.GetProperty(PropertyType.TranslationX).SetDesignValueInternal(node.Position.X);
            domainNode.GetProperty(PropertyType.TranslationY).SetDesignValueInternal(node.Position.Y);
            domainNode.GetProperty(PropertyType.RotationAngle).SetDesignValueInternal(node.Angle);
            domainNode.GetProperty(PropertyType.BoneLength).SetDesignValueInternal(node.BoneLength);
            domainNode.GetProperty(PropertyType.Visibility).SetDesignValueInternal(node.IsVisible ? 1f : 0f);

            return(domainNode);
        }
Exemple #3
0
 private void renderSkeleton(BoneNode parent)
 {
     if(parent.children != null)
     {
         foreach(BoneNode child in parent.children)
         {
             Debug.DrawLine(parent.boneObject.transform.localPosition, child.boneObject.transform.localPosition, Color.green, 2f, false);
             renderSkeleton (child);
         }
     }
 }
Exemple #4
0
 private void renderSkeleton(BoneNode parent)
 {
     if (parent.children != null)
     {
         foreach (BoneNode child in parent.children)
         {
             Debug.DrawLine(parent.boneObject.transform.localPosition, child.boneObject.transform.localPosition, Color.green, 2f, false);
             renderSkeleton(child);
         }
     }
 }
Exemple #5
0
    //tran当前节点,child联动的子节点
    public BoneNode GetNode(Transform tran, Transform child)
    {
        BoneNode node = new BoneNode();

        node.transform     = tran;
        node.localRotation = tran.transform.localRotation;
        node.prePos        = child.position;
        node.curPos        = child.position;
        node.length        = Vector3.Distance(child.position, tran.position);
        return(node);
    }
Exemple #6
0
        public BoneNode[] BuildBoneHierarchy([NotNull] PmxModel pmx)
        {
            var boneList     = new List <BoneNode>();
            var pmxBones     = pmx.Bones;
            var pmxBoneCount = pmxBones.Length;

            var boneNameMapInversion = _boneNameMapInversion;

            for (var i = 0; i < pmxBoneCount; i++)
            {
                var pmxBone = pmxBones[i];
                var parent  = pmxBone.ParentIndex >= 0 ? boneList[pmxBone.ParentIndex] : null;

                boneNameMapInversion.TryGetValue(pmx.Name, out var mltdBoneName);

                var path = mltdBoneName ?? pmxBone.Name;

                Vector3 t;

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

                var bone = new BoneNode(parent, i, path, t, Quaternion.Identity);

                boneList.Add(bone);
            }

            foreach (var bone in boneList)
            {
                var level  = 0;
                var parent = bone.Parent;

                while (parent != null)
                {
                    ++level;
                    parent = parent.Parent;
                }

                bone.Level = level;
            }

            foreach (var bone in boneList)
            {
                bone.Initialize();
            }

            return(boneList.ToArray());
        }
Exemple #7
0
        public IReadOnlyList <BoneNode> BuildBoneHierarchy([NotNull] PmxModel pmx)
        {
            var boneList = new List <BoneNode>();

            for (var i = 0; i < pmx.Bones.Count; i++)
            {
                var pmxBone = pmx.Bones[i];
                var parent  = pmxBone.ParentIndex >= 0 ? boneList[pmxBone.ParentIndex] : null;

                var mltdBoneName = _boneNameMap.SingleOrDefault(kv => kv.Value == pmx.Name).Key;
                var path         = mltdBoneName ?? pmxBone.Name;

                Vector3 t;

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

                var bone = new BoneNode(parent, i, path, t, Quaternion.Identity);

                boneList.Add(bone);
            }

            foreach (var bone in boneList)
            {
                var level  = 0;
                var parent = bone.Parent;

                while (parent != null)
                {
                    ++level;
                    parent = parent.Parent;
                }

                bone.Level = level;
            }

            foreach (var bone in boneList)
            {
                bone.Initialize();
            }

            return(boneList);
        }
Exemple #8
0
        private BoneNode[] BuildBoneHierarchy([NotNull] TransformHierarchies transformHierarchies, bool fixKubi)
        {
            var boneList = new List <BoneNode>();

            {
                var bonePathList = new List <string>();
                var orderedTransformHierarchy = transformHierarchies.GetOrderedObjectArray();

                var scaleUnityToPmx = _scalingConfig.ScaleUnityToPmx;

                for (var i = 0; i < orderedTransformHierarchy.Length; i += 1)
                {
                    var n = orderedTransformHierarchy[i];

                    var bonePath = n.GetFullName();
                    bonePathList.Add(bonePath);

                    var parentPath = bonePath.BreakUntilLast('/');
                    int parentIndex;

                    if (parentPath == bonePath)
                    {
                        // It's a root bone
                        parentIndex = -1;
                    }
                    else
                    {
                        parentIndex = n.Parent != null?bonePathList.IndexOf(parentPath) : -1;
                    }

                    var parent = parentIndex >= 0 ? boneList[parentIndex] : null;

                    var initialPose = n.Transform;

                    var t = initialPose.LocalPosition.ToOpenTK() * scaleUnityToPmx;
                    var q = initialPose.LocalRotation.ToOpenTK();

                    var bone = new BoneNode(parent, i, bonePath, t, q);

                    boneList.Add(bone);
                }
            }

            PostprocessBoneList(boneList, fixKubi, true);

            return(boneList.ToArray());
        }
Exemple #9
0
    private void Awake()
    {
        Transform trans = rootBone ? rootBone : this.transform;

        while (trans.childCount > 0)
        {
            Transform child = trans.GetChild(0);
            BoneNode  node  = new BoneNode();
            node.trans = trans;
            node.originLocalRotation = trans.localRotation;
            node.originLength        = Vector3.Distance(trans.position, child.position);
            node.originRadius        = trans.localScale.x * 0.5f;
            node.prevPos             = child.position;
            node.curPos = child.position;
            nodes.Add(node);
            trans = child;
        }
    }
Exemple #10
0
        public static IReadOnlyList <BoneNode> BuildBoneHierachy([NotNull] PrettyAvatar avatar, [NotNull] PrettyMesh mesh)
        {
            var boneList = new List <BoneNode>();

            for (var i = 0; i < avatar.AvatarSkeleton.Nodes.Length; i++)
            {
                var n = avatar.AvatarSkeleton.Nodes[i];

                var parent   = n.ParentIndex >= 0 ? boneList[n.ParentIndex] : null;
                var boneId   = avatar.AvatarSkeleton.NodeIDs[i];
                var bonePath = avatar.BoneNamesMap[boneId];

                var boneIndex = avatar.AvatarSkeleton.NodeIDs.FindIndex(boneId);

                if (boneIndex < 0)
                {
                    throw new IndexOutOfRangeException();
                }

                var initialPose = avatar.AvatarSkeletonPose.Transforms[boneIndex];

                var bone = new BoneNode(parent, i, bonePath, initialPose.LocalPosition.ToOpenTK(), initialPose.LocalRotation.ToOpenTK());

                boneList.Add(bone);
            }

            foreach (var bone in boneList)
            {
                bone.Initialize();
            }

#if DEBUG
            Debug.Print("Model bones:");

            for (var i = 0; i < boneList.Count; i++)
            {
                var bone = boneList[i];
                Debug.Print("[{0}]: {1}", i, bone.ToString());
            }
#endif

            return(boneList);
        }
Exemple #11
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);
            }
        }
Exemple #12
0
        /// <summary>
        /// Loads the specified file.
        /// </summary>
        private void Load()
        {
            // header
            int    version      = 2;
            string magic_number = fh.Read <ZString>();

            magic_number = magic_number.Substring(0, 7);

            if (magic_number == "ZMD0002")
            {
                version = 2;
            }
            else if (magic_number == "ZMD0003")
            {
                version = 3;
            }


            fh.Seek(7, SeekOrigin.Begin);
            nBones = fh.Read <int>();
            bones  = new List <BoneNode>();

            for (int i = 0; i < nBones; i++)
            {
                BoneNode node = new BoneNode();
                node.BoneID   = i;
                node.ParentID = fh.Read <int>();
                node.Name     = fh.Read <ZString>();

                node.Position = new Vector3()
                {
                    x = fh.Read <float>() * zz_scale,
                    y = fh.Read <float>() * zz_scale,
                    z = fh.Read <float>() * zz_scale
                };

                node.Position = Utils.r2uPosition(node.Position);
                node.Rotation = Utils.r2uRotation(new Quaternion()
                {
                    w = fh.Read <float>(),
                    x = fh.Read <float>(),
                    y = fh.Read <float>(),
                    z = fh.Read <float>()
                });

                bones.Add(node);
            }

            // Load Dummy bones
            dummies  = new List <BoneNode>();
            nDummies = fh.Read <int>();
            for (int i = 0; i < nDummies; i++)
            {
                BoneNode node = new BoneNode();
                node          = new BoneNode();
                node.BoneID   = nBones + i;
                node.Name     = fh.Read <ZString>();
                node.ParentID = fh.Read <int>();

                node.Position = new Vector3()
                {
                    x = fh.Read <float>() * zz_scale,
                    y = fh.Read <float>() * zz_scale,
                    z = fh.Read <float>() * zz_scale
                };

                node.Position = Utils.r2uPosition(node.Position);

                if (version == 3)
                {
                    node.Rotation = Utils.r2uRotation(new Quaternion()
                    {
                        w = fh.Read <float>(),
                        x = fh.Read <float>(),
                        y = fh.Read <float>(),
                        z = fh.Read <float>()
                    });
                }
                dummies.Add(node);
            }


            // add extra root bone dummy bone
            BoneNode extraDummy = new BoneNode();

            extraDummy.Name     = "_p1";
            extraDummy.ParentID = 0;
            extraDummy.Position = new Vector3();
            extraDummy.Rotation = Quaternion.identity;
            extraDummy.BoneID   = nBones + nDummies;

            dummies.Add(extraDummy);

            nDummies = dummies.Count;

            fh.Close();

            if (asset != null)
            {
                Resources.UnloadAsset(asset);
            }
        }
Exemple #13
0
        private VmdBoneFrame[] CreateBoneFrames([NotNull] IBodyAnimationSource mainDance, [NotNull] PrettyAvatar avatar, [NotNull] PmxModel pmx, [NotNull] ScenarioObject baseScenario, [CanBeNull] ScenarioObject formationInfo, [CanBeNull] IBodyAnimationSource danceAppeal, int formationNumber, AppealType appealType)
        {
            var boneLookup = new BoneLookup(_conversionConfig);

            var mltdHierarchy = boneLookup.BuildBoneHierarchy(avatar);

            mltdHierarchy.AssertAllUnique();

            var pmxHierarchy = boneLookup.BuildBoneHierarchy(pmx);

            pmxHierarchy.AssertAllUnique();

            if (_conversionConfig.AppendIKBones || _conversionConfig.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.Length == pmxHierarchy.Length, "Hierarchy number should be equal between MLTD and MMD.");
            }

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

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

            var mainAnimation     = mainDance.Convert();
            var appealAnimation   = danceAppeal?.Convert();
            var mltdBoneCount     = mltdHierarchy.Length;
            var animatedBoneCount = mainAnimation.BoneCount;
            var keyFrameCount     = mainAnimation.KeyFrames.Length;

            {
                var names1 = mainAnimation.KeyFrames.Take(animatedBoneCount)
                             .Select(kf => kf.Path).ToArray();
                var names = names1.Select(boneLookup.GetVmdBoneNameFromBonePath).ToArray();

                // Mark MLTD key bones.
                foreach (var name in names)
                {
                    MarkNamedBoneAsKeyBone(pmx, name);
                }

                // Special cases
                MarkNamedBoneAsKeyBone(pmx, "KUBI");
                MarkNamedBoneAsKeyBone(pmx, "頭");
            }

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

            // Use this value to export visible frames only
            var resultFrameCount = (int)(mainAnimation.Duration * FrameRate.Mltd);
            // Use this value to export all frames, including invisible frames in normal MVs (e.g. seek targets)
            // var resultFrameCount = keyFrameCount / animatedBoneCount;

            var boneFrameList = new List <VmdBoneFrame>();

            // Reduce memory pressure of allocating new delegates (see mltdHierarchy.FirstOrDefault(...))
            var boneMatchPredicateCache = new Predicate <PmxBone> [mltdBoneCount];

            for (var j = 0; j < mltdBoneCount; j += 1)
            {
                var refBone = pmx.Bones[j];
                boneMatchPredicateCache[j] = bone => bone.Name == refBone.Name;
            }

            // Cache `mltdBoneName`s so we don't have to compute them all in every iteration
            var boneNameCache = new Dictionary <string, string>();

            var transform60FpsTo30Fps = _conversionConfig.Transform60FpsTo30Fps;
            var scaleToVmdSize        = _conversionConfig.ScaleToVmdSize;
            var unityToVmdScale       = _scalingConfig.ScaleUnityToVmd;

            var baseFormationList   = CollectFormationChanges(formationInfo, AppealType.None);
            var appealFormationList = CollectFormationChanges(formationInfo, appealType);
            var appealTimes         = AppealHelper.CollectAppealTimeInfo(baseScenario);
            var seekFrameControls   = CollectSeekFrames(baseScenario, formationNumber);

            var seekFrameCounter = 0;
            var lastSoughtFrame  = -1;

            // OK, now perform iterations
            for (var mltdFrameIndex = 0; mltdFrameIndex < resultFrameCount; ++mltdFrameIndex)
            {
                if (transform60FpsTo30Fps)
                {
                    if (mltdFrameIndex % 2 == 1)
                    {
                        continue;
                    }
                }

                var shouldUseAppeal = appealType != AppealType.None && (appealTimes.StartFrame <= mltdFrameIndex && mltdFrameIndex < appealTimes.EndFrame) && appealAnimation != null;

                var animation = shouldUseAppeal ? appealAnimation : mainAnimation;

                int projectedFrameIndex;

                if (shouldUseAppeal)
                {
                    var indexInAppeal = mltdFrameIndex - appealTimes.StartFrame;

                    if (indexInAppeal >= appealAnimation.FrameCount)
                    {
                        indexInAppeal = appealAnimation.FrameCount - 1;
                    }

                    // `indexInAppeal`, unlike `mltdFrameIndex`, has not been scaled yet
                    if (transform60FpsTo30Fps)
                    {
                        projectedFrameIndex = indexInAppeal / 2;
                    }
                    else
                    {
                        projectedFrameIndex = indexInAppeal;
                    }
                }
                else
                {
                    projectedFrameIndex = CalculateSeekFrameTarget(mltdFrameIndex, seekFrameControls, ref lastSoughtFrame, ref seekFrameCounter);
                }

                var formationList = shouldUseAppeal ? appealFormationList : baseFormationList;

                formationList.TryGetCurrentValue(mltdFrameIndex, out var formations);

                Vector4 idolOffset;

                if (formations == null || formations.Length < formationNumber)
                {
                    idolOffset = Vector4.Zero;
                }
                else
                {
                    idolOffset = formations[formationNumber - 1];
                }

                var keyFrameIndexStart = projectedFrameIndex * animatedBoneCount;

                for (var j = 0; j < animatedBoneCount; ++j)
                {
                    var keyFrame     = animation.KeyFrames[keyFrameIndexStart + j];
                    var mltdBoneName = GetMltdBoneNameWithoutBodyScale(boneNameCache, keyFrame);

                    // Uniqueness is asserted above
                    var targetBone = mltdHierarchy.Find(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)
                        {
                            continue;
                        }

                        var attachmentTarget = kv.Value;

                        // Uniqueness is asserted above
                        transferredBone = mltdHierarchy.Find(bone => bone.Name == attachmentTarget);

                        if (transferredBone == null)
                        {
                            throw new ArgumentException("Cannot find transferred bone.");
                        }

                        break;
                    }

                    if (keyFrame.HasPositions)
                    {
                        // ReSharper disable once PossibleInvalidOperationException
                        var x = keyFrame.PositionX.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        var y = keyFrame.PositionY.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        var z = keyFrame.PositionZ.Value;

                        if (string.Equals(keyFrame.Path, "MODEL_00", StringComparison.Ordinal))
                        {
                            var worldRotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathHelper.DegreesToRadians(idolOffset.W));
                            var newOrigin     = worldRotation * new Vector3(x, y, z);
                            var newPosition   = newOrigin + idolOffset.Xyz;

                            (x, y, z) = (newPosition.X, newPosition.Y, newPosition.Z);
                        }

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

                        t = t.FixUnityToMmd();

                        if (scaleToVmdSize)
                        {
                            t = t * unityToVmdScale;
                        }

                        targetBone.LocalPosition = t;

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

                    if (keyFrame.HasRotations)
                    {
                        // ReSharper disable once PossibleInvalidOperationException
                        var x = keyFrame.AngleX.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        var y = keyFrame.AngleY.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        var z = keyFrame.AngleZ.Value;

                        if (string.Equals(keyFrame.Path, "MODEL_00", StringComparison.Ordinal))
                        {
                            // The W component stores rotation
                            y += idolOffset.W;
                        }

                        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 < mltdBoneCount; ++j)
                {
                    var pmxBone  = pmxHierarchy[j];
                    var mltdBone = mltdHierarchy[j];

                    {
                        var predicate = boneMatchPredicateCache[j];
                        var pb        = pmx.Bones.Find(predicate);

#if DEBUG
                        if (pb == null)
                        {
                            // Lazy evaluation of the assertion message
                            Debug.Assert(pb != null, $"PMX bone with the name \"{pmxBone.Name}\" should exist.");
                        }
#endif

                        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 vmdFrameIndex;

                    if (_conversionConfig.Transform60FpsTo30Fps)
                    {
                        vmdFrameIndex = mltdFrameIndex / 2;
                    }
                    else
                    {
                        vmdFrameIndex = mltdFrameIndex;
                    }

                    var mltdBoneName = GetMltdBoneNameWithoutBodyScale(boneNameCache, mltdBone.Path);
                    var vmdBoneName  = boneLookup.GetVmdBoneNameFromBoneName(mltdBone.Path);
                    var boneFrame    = new VmdBoneFrame(vmdFrameIndex, vmdBoneName);

                    var isMovable = BoneLookup.IsBoneMovable(mltdBoneName);

                    boneFrame.Position = isMovable ? t : Vector3.Zero;
                    boneFrame.Rotation = q;

                    boneFrameList.Add(boneFrame);

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

            return(boneFrameList.ToArray());
        }
Exemple #14
0
        /// <summary>
        /// Loads the specified file.
        /// </summary>
        private void Load()
        {
            // header
            int version = 2;
            string magic_number = fh.Read<ZString>();
            magic_number = magic_number.Substring(0,7);

            if(magic_number == "ZMD0002")
                version = 2;
            else if(magic_number == "ZMD0003")
                version = 3;

            fh.Seek(7, SeekOrigin.Begin);
            nBones = fh.Read<int>();
            bones = new List<BoneNode>();

            for(int i=0; i<nBones; i++)
            {
                BoneNode node = new BoneNode();
                node.BoneID = i;
                node.ParentID = fh.Read<int>();
                node.Name = fh.Read<ZString>();

                node.Position = new Vector3()
                {
                    x = fh.Read<float>() * zz_scale,
                    y = fh.Read<float>() * zz_scale,
                    z = fh.Read<float>() * zz_scale
                };

                node.Position = Utils.r2uPosition(node.Position);
                node.Rotation = Utils.r2uRotation(new Quaternion()
                {
                    w = fh.Read<float>(),
                    x = fh.Read<float>(),
                    y = fh.Read<float>(),
                    z = fh.Read<float>()
                });

                bones.Add (node);
            }

            // Load Dummy bones
            dummies = new List<BoneNode>();
            nDummies  = fh.Read<int>();
            for(int i = 0; i<nDummies; i++)
            {
                BoneNode node = new BoneNode();
                node = new BoneNode();
                node.BoneID = nBones + i;
                node.Name = fh.Read<ZString>();
                node.ParentID = fh.Read<int>();

                node.Position = new Vector3()
                {
                    x = fh.Read<float>() * zz_scale,
                    y = fh.Read<float>() * zz_scale,
                    z = fh.Read<float>() * zz_scale
                };

                node.Position = Utils.r2uPosition(node.Position);

                if(version == 3)
                {
                    node.Rotation = Utils.r2uRotation(new Quaternion()
                    {
                        w = fh.Read<float>(),
                        x = fh.Read<float>(),
                        y = fh.Read<float>(),
                        z = fh.Read<float>()
                    });
                }
                dummies.Add(node);
            }

            // add extra root bone dummy bone
            BoneNode extraDummy = new BoneNode();
            extraDummy.Name = "_p1";
            extraDummy.ParentID = 0;
            extraDummy.Position = new Vector3();
            extraDummy.Rotation = Quaternion.identity;
            extraDummy.BoneID = nBones + nDummies;

            dummies.Add (extraDummy);

            nDummies = dummies.Count;

            fh.Close();

            if( asset != null )
                Resources.UnloadAsset(asset);
        }
Exemple #15
0
        public IReadOnlyList <BoneNode> BuildBoneHierarchy([NotNull] PrettyAvatar avatar, bool fixKubi)
        {
            var boneList      = new List <BoneNode>();
            var skeletonNodes = avatar.AvatarSkeleton.Nodes;

            for (var i = 0; i < skeletonNodes.Length; i++)
            {
                var n = skeletonNodes[i];

                var parent   = n.ParentIndex >= 0 ? boneList[n.ParentIndex] : null;
                var boneId   = avatar.AvatarSkeleton.NodeIDs[i];
                var bonePath = avatar.BoneNamesMap[boneId];

                var boneIndex = avatar.AvatarSkeleton.NodeIDs.FindIndex(boneId);

                if (boneIndex < 0)
                {
                    throw new IndexOutOfRangeException();
                }

                var initialPose = avatar.AvatarSkeletonPose.Transforms[boneIndex];

                var t = initialPose.Translation.ToOpenTK() * _scalingConfig.ScaleUnityToPmx;
                var q = initialPose.Rotation.ToOpenTK();

                var bone = new BoneNode(parent, i, bonePath, t, q);

                boneList.Add(bone);
            }

#if DEBUG
            Debug.Print("Model bones:");

            for (var i = 0; i < boneList.Count; i++)
            {
                var bone = boneList[i];
                Debug.Print("[{0}]: {1}", i, bone.ToString());
            }
#endif

            foreach (var bone in boneList)
            {
                bone.Initialize();
            }

            if (fixKubi)
            {
                // Fix "KUBI" (neck) bone's parent
                var kubiParent = boneList.SingleOrDefault(bn => bn.Path == "MODEL_00/BASE/MUNE1/MUNE2/KUBI");
                var kubiBone   = boneList.SingleOrDefault(bn => bn.Path == "KUBI");

                Debug.Assert(kubiParent != null);
                Debug.Assert(kubiBone != null);

                kubiParent.AddChild(kubiBone);

                Debug.Assert(kubiBone.Parent != null);

                // Don't forget to remove it from its old parent (or it will be updated twice from two parents).
                // The original parents and grandparents of KUBI are not needed; they are just like model anchors and shouldn't be animated.
                // See decompiled model for more information.
                kubiBone.Parent.RemoveChild(kubiBone);

                kubiBone.Parent = kubiParent;

                // Set its new initial parameters.
                // Since the two bones (KUBI and MODEL_00/BASE/MUNE1/MUNE2/KUBI) actually share the exact same transforms,
                // set its local transform to identity (t=0, q=0).
                kubiBone.InitialPosition = Vector3.Zero;
                kubiBone.InitialRotation = Quaternion.Identity;
                kubiBone.LocalPosition   = Vector3.Zero;
                kubiBone.LocalRotation   = Quaternion.Identity;

                foreach (var bone in boneList)
                {
                    bone.Initialize(true);
                }
            }

            foreach (var bone in boneList)
            {
                var level  = 0;
                var parent = bone.Parent;

                while (parent != null)
                {
                    ++level;
                    parent = parent.Parent;
                }

                bone.Level = level;
            }

            return(boneList);
        }
        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);
        }
        private IReadOnlyList <VmdBoneFrame> CreateBoneFrames([NotNull] IBodyAnimationSource bodyMotionSource, [NotNull] PrettyAvatar avatar, [NotNull] PmxModel pmx)
        {
            var boneLookup = new BoneLookup(_conversionConfig);

            var mltdHierarchy = boneLookup.BuildBoneHierarchy(avatar);
            var pmxHierarchy  = boneLookup.BuildBoneHierarchy(pmx);

            if (_conversionConfig.AppendIKBones || _conversionConfig.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         = bodyMotionSource.Convert();
            var boneCount         = mltdHierarchy.Count;
            var animatedBoneCount = animation.BoneCount;
            var keyFrameCount     = animation.KeyFrames.Count;

            {
                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(boneLookup.GetVmdBoneNameFromBonePath).ToArray();
                // Mark MLTD key bones.
                foreach (var name in names)
                {
                    MarkNamedBone(name);
                }

                // Special cases
                MarkNamedBone("KUBI");
                MarkNamedBone("頭");
            }

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

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

            // Reduce memory pressure of allocating new delegates (see mltdHierarchy.FirstOrDefault(...))
            var boneMatchPredicateCache = new Func <PmxBone, bool> [boneCount];

            for (var j = 0; j < boneCount; j += 1)
            {
                var refBone = pmx.Bones[j];
                boneMatchPredicateCache[j] = bone => bone.Name == refBone.Name;
            }

            // Cache `mltdBoneName`s so we don't have to compute them all in every iteration
            var boneNameCache = new Dictionary <string, string>();

            // OK, now perform iterations
            for (var i = 0; i < iterationTimes; ++i)
            {
                if (_conversionConfig.Transform60FpsTo30Fps)
                {
                    if (i % 2 == 1)
                    {
                        continue;
                    }
                }

                var keyFrameIndexStart = i * animatedBoneCount;

                for (var j = 0; j < animatedBoneCount; ++j)
                {
                    var    keyFrame = animation.KeyFrames[keyFrameIndexStart + j];
                    string mltdBoneName;

                    if (boneNameCache.ContainsKey(keyFrame.Path))
                    {
                        mltdBoneName = boneNameCache[keyFrame.Path];
                    }
                    else
                    {
                        if (keyFrame.Path.Contains("BODY_SCALE/"))
                        {
                            mltdBoneName = keyFrame.Path.Replace("BODY_SCALE/", string.Empty);
                        }
                        else
                        {
                            mltdBoneName = keyFrame.Path;
                        }

                        boneNameCache.Add(keyFrame.Path, mltdBoneName);
                    }

                    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)
                    {
                        // ReSharper disable once PossibleInvalidOperationException
                        var x = keyFrame.PositionX.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        var y = keyFrame.PositionY.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        var z = keyFrame.PositionZ.Value;

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

                        t = t.FixUnityToOpenTK();

                        if (_conversionConfig.ScaleToVmdSize)
                        {
                            t = t * _scalingConfig.ScaleUnityToPmx;
                        }

                        targetBone.LocalPosition = t;

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

                    if (keyFrame.HasRotations)
                    {
                        // ReSharper disable once PossibleInvalidOperationException
                        var x = keyFrame.AngleX.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        var y = keyFrame.AngleY.Value;
                        // ReSharper disable once PossibleInvalidOperationException
                        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 predicate = boneMatchPredicateCache[j];
                        var pb        = pmx.Bones.FirstOrDefault(predicate);

#if DEBUG
                        if (pb == null)
                        {
                            // Lazy evaluation of the assertion message
                            Debug.Assert(pb != null, $"PMX bone with the name \"{pmxBone.Name}\" should exist.");
                        }
#endif

                        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.Transform60FpsTo30Fps)
                    {
                        frameIndex = i / 2;
                    }
                    else
                    {
                        frameIndex = i;
                    }

                    var vmdBoneName = boneLookup.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);
        }