Esempio n. 1
0
        public void AddMorph(IPEPluginHost host, Form1 form)
        {
            if (basePos.Count < 1)
            {
                MessageBox.Show("モデル形状に変化がありません"); return;
            }
            IPXMorph nmorph = host.Builder.Pmx.Morph();

            nmorph.Kind  = MorphKind.UV;
            nmorph.Name  = form.MorphName;
            nmorph.Panel = form.MorphPanel;
            var pmx = host.Connector.Pmx.GetCurrentState();

            foreach (var i in basePos.Keys)
            {
                IPXUVMorphOffset offset = host.Builder.Pmx.UVMorphOffset();
                offset.Vertex = pmx.Vertex[i];
                Vector3 offsetVec = morphedPos[i] - basePos[i];
                offset.Offset = new V4(offsetVec, 0);
                nmorph.Offsets.Add(offset);
            }
            pmx.Morph.Add(nmorph);
            host.Connector.Pmx.Update(pmx);
            host.Connector.Form.UpdateList(PEPlugin.Pmd.UpdateObject.All);
            host.Connector.View.PMDView.UpdateModel();
            host.Connector.View.PMDView.UpdateView();
            ReSetUVList(host.Connector, form);
            MessageBox.Show(nmorph.Name + "を追加しました");
        }
Esempio n. 2
0
 private void PopulateList(MorphKind kind)
 {
     _pmx = GetCurrentScene();
     morphList.Items.Clear();
     for (int i = 0; i < _pmx.Morph.Count; ++i)
     {
         IPXMorph morph = _pmx.Morph[i];
         if (MorphIsOfKind(morph, kind))
         {
             ListViewItem item = new ListViewItem(new string[] { morph.Name, morph.NameE });
             item.Tag = i;
             morphList.Items.Add(item);
         }
     }
 }
Esempio n. 3
0
        private bool MorphIsOfKind(IPXMorph morph, MorphKind kind)
        {
            switch (kind)
            {
            case MorphKind.Vertex:
                return(morph.Kind == PEPlugin.Pmx.MorphKind.Vertex);

            case MorphKind.UV:
                return(morph.Kind == PEPlugin.Pmx.MorphKind.UV || morph.Kind == PEPlugin.Pmx.MorphKind.UVA1 || morph.Kind == PEPlugin.Pmx.MorphKind.UVA2 || morph.Kind == PEPlugin.Pmx.MorphKind.UVA3 || morph.Kind == PEPlugin.Pmx.MorphKind.UVA4);

            case MorphKind.BoneRotation:
            case MorphKind.BoneTranslation:
                return(morph.Kind == PEPlugin.Pmx.MorphKind.Bone);

            default:
                return(false);
            }
        }
Esempio n. 4
0
        private void applyButton_Click(object sender, EventArgs e)
        {
            _pmx = GetCurrentScene();
            foreach (ListViewItem item in morphList.SelectedItems)
            {
                int index = (int)item.Tag;
                _pmx = _args.Host.Connector.Pmx.GetCurrentState();

                // Create a working copy to make sure the original remains unaffected
                IPXMorph workingCopy = (IPXMorph)_pmx.Morph[index].Clone();

                // Scale offsets
                switch (SelectedMorphKind)
                {
                case MorphKind.Vertex:
                    foreach (IPXVertexMorphOffset offset in workingCopy.Offsets)
                    {
                        offset.Offset = new V3(offset.Offset.X * (float)scaleX.Value, offset.Offset.Y * (float)scaleY.Value, offset.Offset.Z * (float)scaleZ.Value);
                    }
                    break;

                case MorphKind.UV:
                    // TBI
                    break;

                default:
                    break;
                }

                // Apply morph to the scene
                foreach (IPXVertexMorphOffset offset in workingCopy.Offsets)
                {
                    offset.Vertex.Position += offset.Offset;
                }
            }

            // Update the scene and PMXView
            _args.Host.Connector.Pmx.Update(_pmx);
            _args.Host.Connector.View.PmxView.UpdateModel_Vertex();
            _args.Host.Connector.View.PmxView.UpdateModel_Bone();
        }
Esempio n. 5
0
        internal void LoadUVMorph(IPXMorph morph)
        {
            if (!morph.IsUV)
            {
                throw new ArgumentException("UVモーフ以外のモーフが指定されました。");
            }

            var materialMap = new ConcurrentDictionary <IPXUVMorphOffset, Material>(morph.Offsets.ToDictionary(o => (IPXUVMorphOffset)o, _ => (Material)null));

            materialMap.Keys.AsParallel().ForAll(offset =>
            {
                materialMap[offset] = Materials.First(m => m.Vertices.Contains(offset.Vertex));
            });

            var offsetsGroupByMaterial = materialMap.GroupBy(p => p.Value, p => p.Key);

            foreach (var offsetGroup in offsetsGroupByMaterial)
            {
                Do(offsetGroup.Key, new CommandMoveVerticesByMorph(offsetGroup));
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Executes the provided XmlDocument on the PMX object.
        /// </summary>
        public static RunnerResult Execute(XmlDocument doc, IPXPmx pmx, IPXPmxBuilder builder, RunnerProgress progress)
        {
            XmlElement root = doc.DocumentElement;

            progress.Report(0, "> Execution started.");

            // Begin processing
            // Only the first level of elements are interpreted as commands.
            XmlElement[] commands = root.ChildNodes.OfType <XmlElement>().ToArray();
            for (int i = 0; i < commands.Length; ++i)
            {
                try
                {
                    XmlElement node = commands[i];

                    // The meaningful content of each case constitutes a separate block to avoid mixing variables. Might be unnecessary, but right now I'm not feeling too hot and my output WILL be sloppy.
                    // Should review this code later.
                    switch (node.Name.ToLowerInvariant())
                    {
                    // Creates a new bone with the provided properties.
                    case "bone":
                    {
                        string  name = Math.Abs(Guid.NewGuid().GetHashCode()).ToString();
                        IPXBone bone = null;

                        // Get name and check for collision
                        if (node["name"] != null)
                        {
                            name = node["name"].InnerText;

                            IPXBone existing = pmx.Bone.Where(b => b.Name == name).FirstOrDefault();
                            if (existing != null)
                            {
                                // If there is a collision, refer to the collision attribute
                                switch (node["name"].GetAttribute("collision"))
                                {
                                // If the attribute is "update" or "replace", refer to the existing bone for the rest of the command.
                                case "replace":
                                case "update":
                                    bone = existing;
                                    break;

                                // If the attribute is "skip", don't do anything with the bone and skip the command.
                                case "skip":
                                    continue;

                                // If the attribute is not valid, create a new bone and ignore the old one.
                                default:
                                    bone      = builder.Bone();
                                    bone.Name = bone.NameE = name;
                                    break;
                                }
                            }
                            else
                            {
                                bone      = builder.Bone();
                                bone.Name = bone.NameE = name;
                            }
                        }
                        else
                        {
                            bone      = builder.Bone();
                            bone.Name = bone.NameE = name;
                        }

                        if (node["position"] != null)
                        {
                            bone.Position = node["position"].GetV3();
                        }
                        if (node["translation"] != null)
                        {
                            bone.IsTranslation = bool.TryParse(node["translation"].InnerText, out bool translation) ? translation : false;
                        }
                        if (node["parent"] != null)
                        {
                            bone.Parent = Selector.Bone.Selector(node["parent"], pmx).FirstOrDefault();
                        }
                        if (node["axis"] != null)
                        {
                            V3 v = node["axis"].GetV3();
                            v.Normalize();
                            bone.IsFixAxis = true;
                            bone.FixAxis   = v;
                        }
                        if (node["weight"] != null)
                        {
                            //IEnumerable<IPXVertex> vertices = Selector.Vertex.SelectorList(node["weight"].ChildNodes, pmx);
                            IEnumerable <IPXVertex> vertices = Selector.Vertex.SelectorNode(node["weight"], pmx);
                            foreach (IPXVertex v in vertices)
                            {
                                v.Bone1   = bone;
                                v.Weight1 = 1;
                                v.Bone2   = v.Bone3 = v.Bone4 = null;
                                v.Weight2 = v.Weight3 = v.Weight4 = 0;
                            }
                        }
                        pmx.Bone.Add(bone);
                        progress.Report(Percent(i + 1, commands.Length), string.Format("[bone] Added new bone {0}.", bone.Name));
                    }
                    break;

                    // Assigns the selected vertices to the selected bone using BDEF1.
                    case "weight":
                    {
                        IPXBone bone = null;
                        if (node["target"] != null)
                        {
                            bone = Selector.Bone.Selector(node["target"], pmx).FirstOrDefault();
                            if (bone == null)
                            {
                                progress.Report(Percent(i + 1, commands.Length), string.Format("[weight] Bone was not found.", node["target"].InnerText));
                                break;
                            }
                        }
                        else
                        {
                            progress.Report(Percent(i + 1, commands.Length), "[weight] Bone selector was not specified.");
                            break;
                        }

                        // Set vertex weights
                        if (node["select"] == null)
                        {
                            progress.Report(Percent(i + 1, commands.Length), "[weight] No vertices were selected.");
                            break;
                        }
                        IEnumerable <IPXVertex> vertices = Selector.Vertex.SelectorNode(node["select"], pmx);
                        foreach (IPXVertex v in vertices)
                        {
                            v.Bone1   = bone;
                            v.Weight1 = 1;
                            v.Bone2   = v.Bone3 = v.Bone4 = null;
                            v.Weight2 = v.Weight3 = v.Weight4 = 0;
                        }

                        progress.Report(Percent(i + 1, commands.Length), string.Format("[weight] Weighted {0} vertices to {1} ({2}).", vertices.Count(), bone.Name, bone.NameE));
                    }
                    break;

                    // Creates a new UV morph.
                    case "uvmorph":
                    {
                        // The morph's properties are defined in attributes.
                        IPXMorph morph = builder.Morph();
                        morph.Kind = MorphKind.UV;

                        // Set the UV channel
                        if (int.TryParse(node.GetAttribute("channel"), out int ch))
                        {
                            switch (ch)
                            {
                            case 1:
                                morph.Kind = MorphKind.UVA1;
                                break;

                            case 2:
                                morph.Kind = MorphKind.UVA2;
                                break;

                            case 3:
                                morph.Kind = MorphKind.UVA3;
                                break;

                            case 4:
                                morph.Kind = MorphKind.UVA4;
                                break;

                            default:
                                morph.Kind = MorphKind.UV;
                                break;
                            }
                        }

                        // Set the group panel
                        if (int.TryParse(node.GetAttribute("group"), out int group) && group <= 4 && group >= 0)
                        {
                            morph.Panel = group;
                        }
                        else
                        {
                            switch (node.GetAttribute("group").ToLowerInvariant())
                            {
                            case "hidden":
                            case "hide":
                            case "none":
                            case "0":
                                morph.Panel = 0;
                                break;

                            case "eyebrow":
                            case "eyebrows":
                            case "1":
                                morph.Panel = 1;
                                break;

                            case "eye":
                            case "eyes":
                            case "2":
                                morph.Panel = 2;
                                break;

                            case "mouth":
                            case "3":
                                morph.Panel = 3;
                                break;

                            default:
                                morph.Panel = 4;
                                break;
                            }
                        }

                        // Set the name, ignore collisions
                        if (node.HasAttribute("name"))
                        {
                            morph.Name = morph.NameE = node.GetAttribute("name");
                        }
                        else
                        {
                            morph.Name = morph.NameE = morph.GetHashCode().ToString();
                        }

                        // Each morph contains one or more steps. Each step has its own selectors and transforms, which are applied independently.
                        foreach (XmlElement step in node.ChildNodes.OfType <XmlElement>().Where(el => el.Name.ToLowerInvariant() == "step"))
                        {
                            // Skip steps that have no selectors
                            if (step["select"] == null)
                            {
                                continue;
                            }
                            // Select vertices
                            IEnumerable <IPXVertex> vertices = Selector.Vertex.SelectorNode(step["select"], pmx);
                            foreach (XmlElement e in step)
                            {
                                switch (e.Name.ToLowerInvariant())
                                {
                                case "pan":
                                case "translate":
                                {
                                    V2 v = e.GetV2();
                                    foreach (IPXVertex vertex in vertices)
                                    {
                                        IPXUVMorphOffset offset = builder.UVMorphOffset();
                                        offset.Vertex = vertex;
                                        offset.Offset = new V4(v.X, v.Y, 0, 0);
                                        morph.Offsets.Add(offset);
                                    }
                                }
                                break;

                                case "rotate":              // TODO: UV morph rotation
                                case "scale":               // TODO: UV morph scaling
                                default:
                                    break;
                                }
                            }
                        }

                        pmx.Morph.Add(morph);
                        progress.Report(Percent(i + 1, commands.Length), string.Format("[uvmorph] Created UV morph {0} with {1} offsets.", morph.Name, morph.Offsets.Count));
                    }
                    break;

                    // No particular functionality, testing only.
                    case "test":
                    {
                        //foreach (IPXMorph morph in pmx.Morph.Where(m => m.Kind == MorphKind.UV))
                        //{
                        //    foreach (IPXUVMorphOffset o in morph.Offsets)
                        //    {
                        //        _report(string.Format("{0:f2}, {1:f2}, {2:f2}, {3:f2}", o.Offset.X, o.Offset.Y, o.Offset.Z, o.Offset.W), -1);
                        //    }
                        //}
                    }
                    break;

                    // Sets up the selected vertices for use with AutoLuminous.
                    case "autoluminous":
                    {
                        if (node["select"] == null)
                        {
                            progress.Report(Percent(i + 1, commands.Length), "[weight] No vertices were selected.");
                            break;
                        }
                        IEnumerable <IPXVertex> vertices = Selector.Vertex.SelectorNode(node["select"], pmx);

                        // Set required values.
                        pmx.Header.UVACount = Math.Max(pmx.Header.UVACount, 3);

                        // UV1
                        int   texture        = 0;
                        int   hsv            = 0;
                        float flashFrequency = 0;
                        // UV2
                        V4 baseColor = new V4(0, 0, 0, 0);
                        // UV3
                        float texSubtract = 0;
                        float flashBias   = 0;
                        float push        = 0;

                        // Base emissive color
                        if (node["color"] != null)
                        {
                            baseColor = node["color"].GetV4();
                        }

                        // Base emissive power
                        if (node["power"] != null)
                        {
                            baseColor.W = node["power"].GetSingle();
                        }

                        if (node["texture"] != null)
                        {
                            int.TryParse(node["texture"].InnerText, System.Globalization.NumberStyles.Integer, System.Globalization.NumberFormatInfo.InvariantInfo, out texture);
                        }

                        if (node["colormode"] != null)
                        {
                            switch (node["colormode"].InnerText.ToLowerInvariant())
                            {
                            case "hsv":
                                hsv = 10;
                                break;

                            default:
                                hsv = 0;
                                break;
                            }
                        }

                        if (node["flash"] != null)
                        {
                            float.TryParse(node["flash"].GetAttribute("bias"), System.Globalization.NumberStyles.Float, System.Globalization.NumberFormatInfo.InvariantInfo, out flashBias);
                            flashFrequency = node["flash"].GetSingle();
                        }

                        if (node["subtract"] != null)
                        {
                            texSubtract = node["subtract"].GetSingle();
                        }

                        if (node["push"] != null)
                        {
                            push = node["push"].GetSingle();
                        }

                        foreach (IPXVertex v in vertices)
                        {
                            v.UVA1.X = 0.2f;            // Required value
                            v.UVA1.Y = 0.7f;            // Required value
                            v.UVA1.Z = flashFrequency;
                            v.UVA1.W = texture + hsv;

                            v.UVA2 = baseColor;

                            v.UVA3.X = texSubtract;
                            v.UVA3.Y = flashBias;
                            v.UVA3.Z = push;
                        }
                    }
                    break;

                    // Creates a vertex morph for AutoLuminous properties.
                    case "almorph":
                    {
                        if (node["select"] == null)
                        {
                            progress.Report(Percent(i + 1, commands.Length), "[weight] No vertices were selected.");
                            break;
                        }
                        HashSet <IPXVertex> vertices = new HashSet <IPXVertex>(Selector.Vertex.SelectorNode(node["select"], pmx));

                        V4 color = new V4();

                        if (node["color"] != null)
                        {
                            color = node["color"].GetV4();
                        }

                        // TODO: Implement the rest of the AL properties in ALMorph

                        IPXMorph morph = builder.Morph();
                        morph.Kind = MorphKind.UVA2;
                        if (node.HasAttribute("name"))
                        {
                            morph.Name = morph.NameE = node.GetAttribute("name");
                        }
                        else
                        {
                            morph.Name = morph.NameE = morph.GetHashCode().ToString();
                        }

                        foreach (IPXVertex v in vertices)
                        {
                            // Set required values in case the vertex isn't already AL-enabled
                            v.UVA1.X = 0.2f;
                            v.UVA1.Y = 0.7f;
                            // Create the offset
                            morph.Offsets.Add(builder.UVMorphOffset(v, color));
                        }
                    }
                    break;

                    // Sets environment variables.
                    case "settings":
                    {
                        foreach (XmlElement child in node.ChildNodes.OfType <XmlElement>())
                        {
                            switch (child.Name.ToLowerInvariant())
                            {
                            case "exception":
                                break;

                            case "regex":
                                bool regexCI = GlobalSettings.RegexCaseInsensitive;
                                bool.TryParse(child.GetAttributeCI("caseinsensitive"), out regexCI);
                                break;

                            case "colortype":
                                GlobalSettings.ColorIsFloat = child.InnerText.ToLowerInvariant() == "float";
                                break;

                            default:
                                break;
                            }
                        }
                    }
                    break;

                    // Displays a MessageBox with the provided content. Optionally perform certain actions based on user input.
                    case "mbox":
                    case "messagebox":
                    case "prompt":
                    {
                        if (int.TryParse(node.GetAttributeCI("skip"), out int skipNumber) && skipNumber > 0)
                        {
                            StringBuilder sb = new StringBuilder(string.Format("Would you like to skip the following {0} command(s)?\n", skipNumber));
                            for (int j = 1; j <= skipNumber; ++j)
                            {
                                sb.Append(commands[i + j].Name);
                            }
                            MessageBox.Show(sb.ToString(), "Prompt: skip", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                        }
                        else
                        {
                            MessageBox.Show(node.InnerText, "Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        }
                    }
                    break;

                    // Prints a line in the console.
                    case "echo":
                    case "print":
                        progress.Report(Percent(i + 1, commands.Length), node.InnerText);
                        break;

                    default:
                        progress.Report(Percent(i + 1, commands.Length), "Skipping unknown command " + node.Name);
                        break;
                    }
                }
                catch (Exception ex)
                {
                    if (GlobalSettings.Exception == GlobalSettings.ErrorHandling.Ask)
                    {
                        if (MessageBox.Show(string.Format("The following exception has occured:\n\n{0}\n\nWould you like to continue execution?", ex.ToString()), "Exception", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Error) == System.Windows.Forms.DialogResult.No)
                        {
                            return(RunnerResult.Fail);
                        }
                    }
                    else if (GlobalSettings.Exception == GlobalSettings.ErrorHandling.Abort)
                    {
                        return(RunnerResult.Fail);
                    }
                }
            }

            // If control has reached this point, the execution is considered successful.
            return(RunnerResult.Success);
        }
Esempio n. 7
0
        private void scaleButton_Click(object sender, EventArgs e)
        {
            // Only a quaternion's magnitude can be scaled - notify the user
            if (SelectedMorphKind == MorphKind.BoneRotation && !uniformCheck.Checked && MessageBox.Show("For bone rotation morphs, only the magnitude can be scaled, and only uniformly. If you proceed, the rotation magnitude will be scaled by the factor in the X field.\n\nWould you like to proceed?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
            {
                return;
            }

            _pmx = GetCurrentScene();
            // If new morphs are added, update the list automatically
            bool updateList = false;

            foreach (ListViewItem item in morphList.SelectedItems)
            {
                int index = (int)item.Tag;

                // Create a working copy to make sure the original remains unaffected
                IPXMorph workingCopy = (IPXMorph)_pmx.Morph[index].Clone();

                switch (SelectedMorphKind)
                {
                case MorphKind.Vertex:
                    foreach (IPXVertexMorphOffset offset in workingCopy.Offsets)
                    {
                        offset.Offset = new V3(offset.Offset.X * (float)scaleX.Value, offset.Offset.Y * (float)scaleY.Value, offset.Offset.Z * (float)scaleZ.Value);
                    }
                    break;

                case MorphKind.UV:
                    // TBI
                    break;

                case MorphKind.BoneRotation:
                    foreach (IPXBoneMorphOffset offset in workingCopy.Offsets)
                    {
                        // Scale the rotation's magnitude
                        Q     q     = offset.Rotation;
                        float theta = 2 * (float)Math.Acos(q.W);
                        offset.Rotation.W = (float)Math.Cos((theta * (float)scaleX.Value) / 2);

                        //float s = (float)Math.Sin(theta / 2);
                        //V3 axis = new V3();
                        //axis.X = q.X / s;
                        //axis.Y = q.Y / s;
                        //axis.Z = q.Z / s;
                    }
                    break;

                case MorphKind.BoneTranslation:
                    foreach (IPXBoneMorphOffset offset in workingCopy.Offsets)
                    {
                        offset.Translation = new V3(offset.Translation.X * (float)scaleX.Value, offset.Translation.Y * (float)scaleY.Value, offset.Translation.Z * (float)scaleZ.Value);
                    }
                    break;

                default:
                    break;
                }

                if (createNewCheck.Checked)
                {
                    // Add a new morph
                    workingCopy.Name  += " scaled";
                    workingCopy.NameE += " scaled";
                    _pmx.Morph.Add(workingCopy);
                    updateList = true;
                }
                else
                {
                    // Replace the old morph with the clone
                    _pmx.Morph[index] = workingCopy;
                }
            }

            if (updateList)
            {
                PopulateList(SelectedMorphKind);
            }

            // Commit the changes to the PMX scene
            _args.Host.Connector.Pmx.Update(_pmx);
            _args.Host.Connector.Form.UpdateList(PEPlugin.Pmd.UpdateObject.Morph);
        }
Esempio n. 8
0
 public IPXGroupMorphOffset GroupMorphOffset(IPXMorph morph, float ratio)
 {
     throw new NotImplementedException();
 }
Esempio n. 9
0
 public IPXMorphNodeItem MorphNodeItem(IPXMorph morph)
 {
     throw new NotImplementedException();
 }
Esempio n. 10
0
        private void ButtonExecute_Click(object sender, EventArgs e)
        {
            IPXPmx pmx = args.Host.Builder.Pmx.Pmx();

            pmx.ModelInfo.ModelName = textBoxModelName.Text;
            pmx.ModelInfo.Comment   = textBoxModelText.Text;

            // 作成したpmxにマテリアルをコピー
            foreach (int i in listBoxMaterial.SelectedIndices)
            {
                pmx.Material.Add(currentPmx.Material[i]);
            }

            // マテリアルに含まれる頂点のリストを作成
            List <IPXVertex> vertices = new List <IPXVertex>();

            foreach (var m in pmx.Material)
            {
                foreach (var f in m.Faces)
                {
                    vertices.Add(f.Vertex1);
                    vertices.Add(f.Vertex2);
                    vertices.Add(f.Vertex3);
                }
            }
            // 重複を除去
            vertices = vertices.Distinct().ToList();
            foreach (var v in vertices)
            {
                pmx.Vertex.Add(v);
            }

            // 頂点のウェイトに基づいてボーンをコピー
            if (checkBoxBone.Checked)
            {
                if (radioAllBone.Checked)
                {
                    foreach (var b in currentPmx.Bone)
                    {
                        pmx.Bone.Add(b);
                    }
                    if (checkBoxPhysics.Checked)
                    {
                        foreach (var b in currentPmx.Body)
                        {
                            pmx.Body.Add(b);
                        }
                        foreach (var j in currentPmx.Joint)
                        {
                            pmx.Joint.Add(j);
                        }
                    }
                }
                else
                {
                    // 元のボーン順を保存
                    Dictionary <IPXBone, int> boneIndex = new Dictionary <IPXBone, int>();
                    for (int i = 0; i < currentPmx.Bone.Count; i++)
                    {
                        boneIndex.Add(currentPmx.Bone[i], i);
                    }

                    // 関連ボーン
                    List <IPXBone> bones = new List <IPXBone>();
                    foreach (var v in vertices)
                    {
                        if (v.Bone1 != null)
                        {
                            bones.Add(v.Bone1);
                        }
                        if (v.Bone2 != null)
                        {
                            bones.Add(v.Bone2);
                        }
                        if (v.Bone3 != null)
                        {
                            bones.Add(v.Bone3);
                        }
                        if (v.Bone4 != null)
                        {
                            bones.Add(v.Bone4);
                        }
                    }
                    bones = bones.Distinct().ToList();


                    if (checkBoxPhysics.Checked)
                    {
                        // 関連ボーンの関連剛体
                        List <IPXBody> bodies = new List <IPXBody>();
                        foreach (var b in bones)
                        {
                            bodies.AddRange(currentPmx.Body.Where(x => x.Bone == b));
                        }
                        bodies = bodies.Distinct().ToList();
                        // 関連剛体の関連ジョイント
                        List <IPXJoint> joints = new List <IPXJoint>();
                        foreach (var b in bodies)
                        {
                            joints.AddRange(currentPmx.Joint.Where(x => x.BodyA == b || x.BodyB == b));
                        }
                        joints = joints.Distinct().ToList();
                        // ジョイントに必要な剛体がbodiesに不足していた場合追加
                        foreach (var j in joints)
                        {
                            if (!bodies.Any(b => b == j.BodyA))
                            {
                                bodies.Add(j.BodyA);
                            }
                            if (!bodies.Any(b => b == j.BodyB))
                            {
                                bodies.Add(j.BodyB);
                            }
                        }
                        // 剛体に必要なボーンがbonesに不足していた場合追加
                        foreach (var b in bodies)
                        {
                            if (!bones.Any(x => x == b.Bone))
                            {
                                bones.Add(b.Bone);
                            }
                        }

                        // 剛体・ジョイントの並び順を戻す
                        Dictionary <IPXBody, int> bodyIndex = new Dictionary <IPXBody, int>();
                        for (int i = 0; i < currentPmx.Body.Count; i++)
                        {
                            bodyIndex.Add(currentPmx.Body[i], i);
                        }
                        Dictionary <IPXJoint, int> jointIndex = new Dictionary <IPXJoint, int>();
                        for (int i = 0; i < currentPmx.Joint.Count; i++)
                        {
                            jointIndex.Add(currentPmx.Joint[i], i);
                        }

                        bodies.Sort((a, b) => bodyIndex[a] - bodyIndex[b]);
                        joints.Sort((a, b) => jointIndex[a] - jointIndex[b]);

                        foreach (var b in bodies)
                        {
                            pmx.Body.Add(b);
                        }
                        foreach (var j in joints)
                        {
                            pmx.Joint.Add(j);
                        }
                    }

                    bones.Sort((a, b) => boneIndex[a] - boneIndex[b]);

                    if (radioRelationBone.Checked)
                    {
                        foreach (var b in bones)
                        {
                            pmx.Bone.Add(b);
                        }
                    }

                    if (radioRelationBoneAndParent.Checked)
                    {
                        List <IPXBone> bonesWithParent = new List <IPXBone>();
                        //関連ボーンの親を遡って保持
                        for (int i = 0; i < bones.Count; i++)
                        {
                            IPXBone b      = bones[i];
                            IPXBone forcus = b;
                            while (forcus != null)
                            {
                                if (bonesWithParent.Exists(x => x == forcus))
                                {
                                    break;
                                }
                                bonesWithParent.Add(forcus);
                                if (forcus.AppendParent != null && !bones.Exists(x => x == forcus.AppendParent))
                                {
                                    bones.Add(forcus.AppendParent);
                                }
                                forcus = forcus.Parent;
                            }
                        }

                        bonesWithParent.Sort((a, b) => boneIndex[a] - boneIndex[b]);
                        foreach (var b in bonesWithParent)
                        {
                            pmx.Bone.Add(b);
                        }
                    }
                }
            }

            // モーフをコピー
            if (checkBoxMorph.Checked)
            {
                foreach (var m in currentPmx.Morph)
                {
                    IPXMorph morph       = m;
                    bool     morphExists = true;
                    switch (m.Kind)
                    {
                    case MorphKind.Vertex:
                        List <IPXVertexMorphOffset> vmo = m.Offsets.ToList().ConvertAll(o => (IPXVertexMorphOffset)o);
                        foreach (var o in vmo)
                        {
                            if (!vertices.Exists(v => v == o.Vertex))
                            {
                                morph.Offsets.Remove(o);
                            }
                        }
                        break;

                    case MorphKind.UV:
                    case MorphKind.UVA1:
                    case MorphKind.UVA2:
                    case MorphKind.UVA3:
                    case MorphKind.UVA4:
                        List <IPXUVMorphOffset> umo = m.Offsets.ToList().ConvertAll(o => (IPXUVMorphOffset)o);
                        foreach (var o in umo)
                        {
                            if (!vertices.Exists(v => v == o.Vertex))
                            {
                                morph.Offsets.Remove(o);
                            }
                        }
                        break;

                    case MorphKind.Material:
                        List <IPXMaterialMorphOffset> mmo = m.Offsets.ToList().ConvertAll(o => (IPXMaterialMorphOffset)o);
                        foreach (var o in mmo)
                        {
                            if (!pmx.Material.Contains(o.Material))
                            {
                                morph.Offsets.Remove(o);
                            }
                        }
                        break;

                    default:
                        morphExists = false;
                        break;
                    }
                    if (morphExists && morph.Offsets.Count != 0)
                    {
                        pmx.Morph.Add(morph);
                    }
                }
            }

            Update(args.Host.Connector, pmx, PmxUpdateObject.All);
            MessageBox.Show("完了");
            Reload();
        }