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