DTXMania / MikuMikuFlex

C#用のMikuMikuDance描画ライブラリ。DirectX11対応。
Other
25 stars 9 forks source link

いくつかは間違ったモデルのポーズを助けます。プロジェクトがフォワードキネマティクスの間違った計算を使用しているため。 (Some help with wrong model pose. Because project using wrong calculation of forward kinematics.) #3

Open seemees opened 4 years ago

seemees commented 4 years ago

///JAPAN このコードにエラーがあります。

            #region " 親付与によるFKを適用する。"
            //----------------
            this._親付与によるFK変形更新.変形を更新する();
            this._モデルポーズを再計算する();
            //----------------
            #endregion

このコードにはコメントが必要です

            #region " 親付与によるFKを適用する。"
            //----------------
            //this._親付与によるFK変形更新.変形を更新する();
            //this._モデルポーズを再計算する();
            //----------------
            #endregion

そして、いくつかの変更を行う必要があります 拡張:

FKはIKの前とIKの間に呼び出す必要があります。

1)クラス「ボーンコントロール」では、「親ボーンの子を許可」のリストが必要であり、このリストは「子ボーンのリスト」と同じように入力されますが、「親ボーン」の代わりに「親ボーンを許可」を受け取ります(フォワードキネマティクスで使用されます) ) 2)クラス "ボーンコントロール"に "親の付与されたボーン"、 "付与された位置"、および "付与された回転"変数を追加する必要があります(クラスコンストラクタではそれらをゼロにしますが、 "親の付与されたボーン"は計算子ボーンループで埋める必要があります) 3)手順「モデルポーズの計算」では、 3.1)「許可された回転」と「許可された位置」を、「親許可された骨」から「この骨」への順運動学を介して計算します(「許可された親骨」がnullでない場合のみ)。 3.2)位置+回転(アニメーション、インバースキネマティクス、物理)に加えて、「許可された位置」+「許可された回転」(位置+許可された位置の回転許可された回転)によって骨の新しいローカル位置を計算します。 3.3)親ボーンからグローバルポジションを計算します。 3.4)すべての子ボーンをループして計算します。 3.5)すべての影響を受けるボーンをループして計算します(リスト「親のボーンの子を許可する」を使用)。 4)ドロープロシージャのフォワードキネマティクスを削除する必要があります。 5)ボーンの新しい位置と新しい回転を取得した後の物理演算では、位置から「許可された位置」(ボーン位置=ボーン位置-ボーン「許可された位置」)と回転から「許可された回転」(ボーン回転=ボーン回転)を削除する必要があります。 反転したボーンの「許可された回転」)。

それで全部です。

///ENGLISH In this code error:

            #region " 親付与によるFKを適用する。"
            //----------------
            this._親付与によるFK変形更新.変形を更新する();
            this._モデルポーズを再計算する();
            //----------------
            #endregion

This code must be commented

            #region " 親付与によるFKを適用する。"
            //----------------
            //this._親付与によるFK変形更新.変形を更新する();
            //this._モデルポーズを再計算する();
            //----------------
            #endregion

And some changes need to be done

Explanation:

FK must call before IK and during IK.

1) in class "bone control" need list of "grant parent bone childs" and this list filled same way as "list of child bones", but instead of "parent bone" take "grant parent bone" (that used in forward kinematics)
2) in class "bone control" need to be add "parent granted bone", "granted position" and "granted rotation" variables (in class contstructor zero them, but "parent granted bone" need to be filled in compute child bone loop) 3) in procedure "calculate model pose" we must 3.1) calculate "granted rotation" and "granted position" via forward kinematics from "parent granted bone" to "this bone" (only if "granted parent bone" is not null); 3.2) calculate new local position of bone via position+rotation (animation, inverse kinematics, physics) plus "granted position" + "granted rotation" (position + granted position rotation granted rotation); 3.3) calculate global position from parent bone; 3.4) loop and calculate all child bones; 3.5) loop and calculate all influented bones (via list "grant parent bone child"); 4) Forward kinematics in Draw procedure need to be removed; 5) In Physics calculation after get new position and new rotation of bones we need to remove "granted position" from position (bone position = bone position - bone "granted position") and "granted rotation" from rotation (bone rotation = bone rotation inverted bone "granted rotation").

Thats all.

seemees commented 4 years ago

ファイル1の変更点 (File 1 changings)

//////////////////////////////////////////////////////// //File 1 PMX_model.cs (PMXモデル.cs)

            #region " 親付与によるFKを適用する。"
            //not needed any more
            //----------------
            //this._親付与によるFK変形更新.変形を更新する();
            //this._モデルポーズを再計算する();
            //----------------
            #endregion
seemees commented 4 years ago

ファイル2の変更点 (File 2 changings)

////////////////////////////////////////////////////////
//File 2 PMX_bone_control.cs (PMXボーン制御.cs)

    public class PMX_bone_control : IDisposable
    //public class PMXボーン制御 : IDisposable
    {
    ...
    //++
        //this is grant parent bone (with influence on that bone via influence rate)
        public PMX_bone_control grantedParentBone { get; protected set; }

        //this is the list dependent bones from this bone
        public List<PMX_bone_control> dependentBoneList { get; protected set; }

        public Vector3 dependentPosition { get; set; }
        public Vector3 oldDependantPosition { get; set; }

        public Quaternion dependentRotation
        {
            get { return _dependentRotation; }
            set
            {
                _dependentRotation = value;
                _dependentRotation.Normalize();
            }
        }

        private Quaternion _dependentRotation;

        public Quaternion oldDependentRotation
        {
            get { return _oldDependentRotation; }
            set
            {
                _oldDependentRotation = value;
                _oldDependentRotation.Normalize();
            }
        }
        private Quaternion _oldDependentRotation;

    //--
    ...

        public PMX_bone_control( PMXFormat.Bone bone, int index )
    //public PMXボーン制御( PMXFormat.ボーン bone, int index )
        {
        ...
            //++
            this.dependentBoneList = new List<PMX_bone_control>();  //new List<PMXボーン制御>();
            this.dependentPosition = Vector3.Zero;
            this.dependentRotation = Quaternion.Identity;
            this.oldDependantPosition = Vector3.Zero;
            this.oldDependentRotation = Quaternion.Identity;
            //--
            ...
    }

        internal void PerformPostLoadProcessing( PMX_bone_control[] all_bones )
        //internal void 読み込み後の処理を行う( PMXボーン制御[] 全ボーン )
        {

            //++
            for ( int i = 0; i < all_bones.Length; i++)    //全ボーン.Length
            {
                int parentIndex = all_bones[i].PMXF_bone.parentBoneIndex;   //全ボーン[ i ].PMXFボーン.親ボーンのインデックス
                int grantParentIndex = all_bones[i].PMXF_bone.grant_parent_bone_index;  //全ボーン[ i ].PMXFボーン. grant parent bone index

                if (parentIndex == this.boneIndex || grantParentIndex == this.boneIndex)    // this.boneIndex = this.ボーンインデックス
                {
                    if(parentIndex == this.boneIndex)
                    {
                        all_bones[i].parentBone = this;         //全ボーン[ i ].親ボーン = this;
                        this.childBoneList.Add(all_bones[i]);   //this.子ボーンリスト.Add( 全ボーン[ i ] );
                    }
                    if (grantParentIndex == this.boneIndex)
                    {
                        all_bones[i].grantedParentBone = this;      //全ボーン[ i ].grantedParentBone = this;
                        this.dependentBoneList.Add(all_bones[i]);   //this.dependentBoneList.Add(全ボーン[ i ])
                    }
                }
            }
            //--
            ...
        }

        public virtual void Dispose()
        {
            ...
            //++
            this.grantedParentBone = null;
            this.dependentBoneList = null;
            //--
            ...
    }

        internal void CalculateModelPose()
    //internal void モデルポーズを計算する()
        {

            //Allpy Forward kinematics from influens grantedParentBone
            if (this.grantedParentBone != null)
            {
                if (this.PMXF_bone.moved_granted)
                {
                    var pp = this.grantedParentBone;
                    if(this.oldDependantPosition != pp.position)
                    {
                        this.dependentPosition = Vector3.Lerp(Vector3.Zero, pp.position, this.PMXF_bone.payAngleRate);
                        this.oldDependantPosition = pp.position;
                    }
                }
                else if (this.PMXF_bone.rotationGranted)
                {
                    var pp = this.grantedParentBone;
                    if(this.oldDependentRotation!= pp.rotation)
                    {
                        this.dependentRotation = Quaternion.Slerp(Quaternion.Identity, pp.rotation, this.PMXF_bone.payAngleRate);
                        this.oldDependentRotation = pp.rotation;
                    }
                }
            }
            //now calculate my new local pose (from parent)
            this.localPoseMatrix =
                    Matrix.Translation(-this.localPosition) *           // Return to origin
                    Matrix.RotationQuaternion(this.rotation) *          // Rotate from animation and IK
                    Matrix.RotationQuaternion(this.dependentRotation) *             //rotate from FK (forward kinematics)
                    Matrix.Translation(this.position + this.dependentPosition) *    // Translation from animation + FK 
                    Matrix.Translation(this.localPosition);             // Return to original position

        //now calculate global pose
            if (this.parentBone == null) {
                this.matrixModelPose =
                    this.localPoseMatrix;
            }
            else
            {
               this.matrixModelPose =
                   this.localPoseMatrix *
                   this.parentBone.matrixModelPose;    // If there is a parent bone, reflect the model pose of the parent bone.
            }

            // Update for all child bones.
            foreach ( var childBone in this.childBoneList )
                childBone.CalculateModelPose();         //モデルポーズを計算する()
            // Update for all dependent bones.
            foreach (var dependentBone in this.dependentBoneList)
                dependentBone.CalculateModelPose();     //モデルポーズを計算する()

    }

        ....

     }
seemees commented 4 years ago

ファイル3の変更点 (File 3 changings)

////////////////////////////////////////////////////////
////File 3 PMX_physical_deformation_update.cs (PMX物理変形更新.cs)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using SharpDX;
using BulletSharp;

namespace MikuMikuFlex3
{
    class PMX_physical_deformation_update : IDisposable
    {
    ...

        public void UpdateDeformation()
        {

            // (3) 物理演算の結果に合わせてボーンの位置を修正

            for( int i = 0; i < this._Bulletの剛体リスト.Count; ++i )
            {
        ....

                if( float.IsNaN( globalPose.M11 ) )
                {
                    if( !_physicsAsserted )
                        Debug.WriteLine( "物理演算の結果が不正な結果を出力しました。\nPMXの設定を見直してください。うまくモーション動作しない可能性があります。" );
                    _physicsAsserted = true;
                    continue;
                }
        //++ change block 1
        //old code (I think no Identity if no parent - but THIS BONE global position instead of)
                //var localPose = globalPose * ( ( null != bone.親ボーン ) ? Matrix.Invert( bone.親ボーン.モデルポーズ行列 ) : Matrix.Identity );
                var localPose = globalPose * ( ( null != bone.親ボーン ) ? Matrix.Invert( bone.親ボーン.モデルポーズ行列 ) : Matrix.Invert( bone.モデルポーズ行列 ) );
        //-- change block 1
                var mat = Matrix.Translation( bone.ローカル位置 ) * localPose * Matrix.Translation( -bone.ローカル位置 );
                bone.移動 = new Vector3( mat.M41, mat.M42, mat.M43 );
                bone.回転 = Quaternion.RotationMatrix( mat );
                //++ change block 2
                //remove influence of the FK
                bone.移動 = bone.移動 - bone.dependentPosition;
                bone.回転 = bone.回転 * Quaternion.Invert(bone.dependentRotation);
                //-- change block 2

                bone.モデルポーズを計算する();

            }

        }

    }

}
seemees commented 4 years ago

この変更をテストする前にプロジェクトを保存してください さまざまなmmdモデルを確認してください。前方のキネマティクスが壊れた場合、足が回転しない場合、足先の端がモデルから逃げる場合などがあります。 正しいフォワードキネマティックがすべてより良い場合にのみ。

please save project before testing this changes please check on different mmd models, because if forward kinematics broke, legs do not rotate, end of toe run away from model etc. Only if right forward kinematic used THAN all be good.

DTXMania commented 4 years ago

Thank you for your advice and translation! FK and Grant is the area I don't understand the most in PMX, so I will take some time to understand. Please wait a moment.

DTXMania commented 4 years ago

PMX has a "post-physical deformation" property, but the current MMF does not implement the processing when this is true. (Currently, all deformations are performed before physics calculations.) Also, PMX has an "external parent deformation" property, which is not implemented.

I think these also need to be renovated...

DTXMania commented 4 years ago

I feel that the grant in PMX is different from FK. For example, in a PMX grant hierarchy, parents grant to children but not grandchildren.

seemees commented 4 years ago

そう、そのとおり。 ただし、すべての物理前変形では、各ボーンは2つの「親」ボーンからの移動と回転を蓄積します。 1つの「親」ボーンは「直接親ボーン」(ボーンの部分的なグローバルポーズ1 =ボーンのローカルポーズ+直接親ボーンのグローバルポーズ)、2番目の「親」ボーンは「許可された親ボーン」(および部分的なグローバルポーズ) ボーン2 =許可された親ボーンのグローバルポーズ*レート;レート--1.0から1.0の影響レートです。 次に、骨のグローバルポーズ=骨1の部分的なグローバルポーズ+骨2の部分的なグローバルポーズです。物理計算後は物理計算後に変形せず、物理演算後に変形しますが、2つの親ボーンからポーズを再び蓄積します( "direct 「および「付与」)。 「外部の親ボーン」について-このタイプの接続は、アクセサリモデル(「外部の親ボーン」を使用しないキャラクターモデル)のみに該当すると思います。銃のmmdモデルをキャラクターの手(他のmmdモデル)に接続するのに便利な「外部親ボーン」など。

Yes you right. But in all pre physics deformation each bone accumulate move and rotation from 2 "parent" bone. One "parent" bone is "direct parent bone" (partial global pose of bone 1 = local pose of bone + global pose of direct parent bone), and second "parent" bone is "granted parent bone" (and partial global pose of bone 2 = global pose of granted parent bone * rate; rate - is influence rate from -1.0 to 1.0). Then global pose of bone = partial global pose of bone 1 + partial global pose of bone 2. I think, that Post physics bone is not deformed before physics calculation, and deformed after physics, but again accumulate pose from 2 parent bones ("direct" and "granted"). That about "external parent bone" - I think, that this type of connection is only for accessories models (character models not using "external parent bone"). "External parent bone" useful to connect gun mmd model with character's hand (other mmd model) and so on.

seemees commented 4 years ago

たぶん、2番目の親の骨の名前を間違えました。 それらは「影響のある親の骨」と呼ばれるべきでした。 回転や位置の一部を子ボーンに転送します。

Maybe I incorrectly named the second parental bone. They should have been called the "affecting parent bone." It transfers part of its rotation or position to its child bones.