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); }
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); } } }
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); } } }
//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); }
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()); }
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); }
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()); }
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; } }
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); }
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); } }
/// <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); } }
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()); }
/// <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); }
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); }