public void 初期化する(PMXモデル model, モーフ管理 morph, スキニング skinning, バッファ管理 bufferManager) { _スキニング = skinning; _Stopwatch = new Stopwatch(); _Stopwatch.Start(); _モーフ管理 = morph; モーションリスト = new List <KeyValuePair <string, モーション> >(); }
public PMXボーン(List <MMDFileParser.PMXModelParser.ボーン> bones, int index, int layer, スキニング skinning) { var me = bones[index]; //このボーン _スキニング = skinning; _スキニング.ボーン配列[index] = this; ボーンインデックス = index; ローカル位置 = me.位置; ボーン名 = me.ボーン名; ローカル軸あり = me.ローカル軸あり; 形階層 = layer; 形階層の物理前後 = me.物理後変形である ? 形階層の物理前後指定種別.物理演算後 : 形階層の物理前後指定種別.物理演算前; if (ローカル軸あり) { DefaultLocalX = me.ローカル軸のX軸の方向ベクトル; DefaultLocalY = Vector3.Cross(me.ローカル軸のZ軸の方向ベクトル, DefaultLocalX); DefaultLocalZ = Vector3.Cross(DefaultLocalX, DefaultLocalY); } if (me.IKボーンである) { skinning.IKボーンリスト.Add(this); // IK関連の情報をボーン(me)から取得 IKボーンである = true; IK単位角 = me.IK単位角rad; _IKターゲットボーンインデックス = me.IKターゲットボーンインデックス; IK演算のLoop回数 = me.IKループ回数; foreach (MMDFileParser.PMXModelParser.ボーン.IKリンク ikLink in me.IKリンクリスト) { this.IKリンクリスト.Add(new IKリンク(new WeakReference <スキニング>(skinning), ikLink)); } } 回転付与される = me.回転付与される; 移動付与される = me.移動付与される; if (me.付与親ボーンインデックス == -1) { 回転付与される = false; 移動付与される = false; } if (移動付与される || 回転付与される) { 付与親ボーンインデックス = me.付与親ボーンインデックス; 付与率 = me.付与率; } else { 付与親ボーンインデックス = -1; } for (int i = 0; i < bones.Count; i++) { MMDFileParser.PMXModelParser.ボーン bone = bones[i]; if (bone.親ボーンのインデックス == index) { var child = new PMXボーン(bones, i, layer + 1, skinning); 子ボーンを追加する(child); } } }
public void D3Dスキニングバッファを更新する(スキニング skelton, エフェクト effect) { if (!(D3Dスキニングバッファをリセットする)) { return; } var skinning = (skelton as PMXスケルトン) ?? throw new System.NotSupportedException("PMXバッファ管理クラスでは、スキニングとして PMXスケルトン クラスを指定してください。"); var d3dContext = RenderContext.Instance.DeviceManager.D3DDeviceContext; // エフェクト変数にボーンの情報を設定する。 this._D3DBoneTransデータストリーム.WriteRange(skinning.ボーンのモデルポーズ配列); this._D3DBoneTransデータストリーム.Position = 0; d3dContext.UpdateSubresource(new DataBox(_D3DBoneTransデータストリーム.DataPointer, 0, 0), _D3DBoneTrans定数バッファ, 0); effect.D3DEffect.GetConstantBufferByName("BoneTransBuffer").SetConstantBuffer(this._D3DBoneTrans定数バッファ); this._D3DBoneLocalPositionデータストリーム.WriteRange(skinning.ボーンのローカル位置); this._D3DBoneLocalPositionデータストリーム.Position = 0; d3dContext.UpdateSubresource(new DataBox(_D3DBoneLocalPositionデータストリーム.DataPointer, 0, 0), _D3DBoneLocalPosition定数バッファ, 0); effect.D3DEffect.GetConstantBufferByName("BoneLocalPositionBuffer").SetConstantBuffer(this._D3DBoneLocalPosition定数バッファ); this._D3DBoneQuaternionデータストリーム.WriteRange(skinning.ボーンの回転); this._D3DBoneQuaternionデータストリーム.Position = 0; d3dContext.UpdateSubresource(new DataBox(_D3DBoneQuaternionデータストリーム.DataPointer, 0, 0), _D3DBoneQuaternion定数バッファ, 0); effect.D3DEffect.GetConstantBufferByName("BoneQuaternionBuffer").SetConstantBuffer(this._D3DBoneQuaternion定数バッファ); // 現在の入力頂点リストをスキニングバッファに転送する。 this._頂点データストリーム.WriteRange(入力頂点リスト); this._頂点データストリーム.Position = 0; d3dContext.UpdateSubresource(new DataBox(_頂点データストリーム.DataPointer, 0, 0), D3Dスキニングバッファ, 0); // 使用するtechniqueを検索する。 テクニック technique = (from teq in effect.テクニックリスト where teq.テクニックを適用する描画対象 == MMDPass種別.スキニング select teq).FirstOrDefault(); if (null != technique) { // パスを通じてコンピュートシェーダーステートを設定する。 technique.パスリスト.ElementAt(0).Value.D3DPass.Apply(d3dContext); // コンピュートシェーダーでスキニングを実行し、結果を頂点バッファに格納する。 d3dContext.ComputeShader.SetShaderResource(0, this.D3DスキニングバッファSRView); d3dContext.ComputeShader.SetUnorderedAccessView(0, this.D3D頂点バッファビューUAView); d3dContext.Dispatch((入力頂点リスト.Length / 64) + 1, 1, 1); } // UAVを外す(このあと頂点シェーダーが使えるように) d3dContext.ComputeShader.SetUnorderedAccessView(0, null); #region " (CPUで行ったときのソース)" //---------------- /* * var boneTrans = skinning.ボーンのモデルポーズ配列; * var スキニング後の入力頂点リスト = new VS_INPUT[ 入力頂点リスト.Length ]; * * for( int i = 0; i < 入力頂点リスト.Length; i++ ) * { * switch( 入力頂点リスト[ i ].変形方式 ) * { * case (uint) 変形方式.BDEF1: #region " *** " * //---------------- * { * var 頂点 = 入力頂点リスト[ i ]; * * Matrix bt = * boneTrans[ 頂点.BoneIndex1 ]; * * if( Matrix.Zero == bt ) * bt = Matrix.Identity; * * スキニング後の入力頂点リスト[ i ].Position = Vector4.Transform( 頂点.Position, bt ); * スキニング後の入力頂点リスト[ i ].Normal = Vector3.TransformNormal( 頂点.Normal, bt ); * スキニング後の入力頂点リスト[ i ].Normal.Normalize(); * } * //---------------- #endregion * break; * * case (uint) 変形方式.BDEF2: #region " *** " * //---------------- * { * var 頂点 = 入力頂点リスト[ i ]; * * Matrix bt = * boneTrans[ 頂点.BoneIndex1 ] * 頂点.BoneWeight1 + * boneTrans[ 頂点.BoneIndex2 ] * 頂点.BoneWeight2; * * if( Matrix.Zero == bt ) * bt = Matrix.Identity; * * スキニング後の入力頂点リスト[ i ].Position = Vector4.Transform( 頂点.Position, bt ); * スキニング後の入力頂点リスト[ i ].Normal = Vector3.TransformNormal( 頂点.Normal, bt ); * スキニング後の入力頂点リスト[ i ].Normal.Normalize(); * } * //---------------- #endregion * break; * * case (uint) 変形方式.BDEF4: #region " *** " * //---------------- * { * var 頂点 = 入力頂点リスト[ i ]; * * Matrix bt = * boneTrans[ 頂点.BoneIndex1 ] * 頂点.BoneWeight1 + * boneTrans[ 頂点.BoneIndex2 ] * 頂点.BoneWeight2 + * boneTrans[ 頂点.BoneIndex3 ] * 頂点.BoneWeight3 + * boneTrans[ 頂点.BoneIndex4 ] * 頂点.BoneWeight4; * * if( Matrix.Zero == bt ) * bt = Matrix.Identity; * * スキニング後の入力頂点リスト[ i ].Position = Vector4.Transform( 頂点.Position, bt ); * スキニング後の入力頂点リスト[ i ].Normal = Vector3.TransformNormal( 頂点.Normal, bt ); * スキニング後の入力頂点リスト[ i ].Normal.Normalize(); * } * //---------------- #endregion * break; * * case (uint) 変形方式.SDEF: #region " *** " * //---------------- * { * // 参考: * // 自分用メモ「PMXのスフィリカルデフォームのコードっぽいもの」(sma42氏) * // https://www.pixiv.net/member_illust.php?mode=medium&illust_id=60755964 * * var 頂点 = 入力頂点リスト[ i ]; * #region " 影響度0,1 の算出 " * //---------------- * float 影響度0 = 0f; // 固定値であるSDEFパラメータにのみ依存するので、これらの値も固定値。 * float 影響度1 = 0f; // * { * float L0 = ( 頂点.SdefR0 - (Vector3) skinning.ボーン配列[ 頂点.BoneIndex2 ].ローカル位置 ).Length(); // 子ボーンからR0までの距離 * float L1 = ( 頂点.SdefR1 - (Vector3) skinning.ボーン配列[ 頂点.BoneIndex2 ].ローカル位置 ).Length(); // 子ボーンからR1までの距離 * * 影響度0 = ( System.Math.Abs( L0 - L1 ) < 0.0001f ) ? 0.5f : SharpDX.MathUtil.Clamp( L0 / ( L0 + L1 ), 0.0f, 1.0f ); * 影響度1 = 1.0f - 影響度0; * } * //---------------- #endregion * * Matrix モデルポーズ行列L = boneTrans[ 頂点.BoneIndex1 ] * 頂点.BoneWeight1; * Matrix モデルポーズ行列R = boneTrans[ 頂点.BoneIndex2 ] * 頂点.BoneWeight2; * Matrix モデルポーズ行列C = モデルポーズ行列L + モデルポーズ行列R; * * Vector4 点C = Vector4.Transform( 頂点.Sdef_C, モデルポーズ行列C ); // BDEF2で計算された点Cの位置 * Vector4 点P = Vector4.Transform( 頂点.Position, モデルポーズ行列C ); // BDEF2で計算された頂点の位置 * * Matrix 重み付き回転行列 = Matrix.RotationQuaternion( * Quaternion.Slerp( // 球体線形補間 * skinning.ボーン配列[ 頂点.BoneIndex1 ].回転 * 頂点.BoneWeight1, * skinning.ボーン配列[ 頂点.BoneIndex2 ].回転 * 頂点.BoneWeight2, * 頂点.BoneWeight1 ) ); * * Vector4 点R0 = Vector4.Transform( new Vector4( 頂点.SdefR0, 1f ), ( モデルポーズ行列L + ( モデルポーズ行列C * -頂点.BoneWeight1 ) ) ); * Vector4 点R1 = Vector4.Transform( new Vector4( 頂点.SdefR1, 1f ), ( モデルポーズ行列R + ( モデルポーズ行列C * -頂点.BoneWeight2 ) ) ); * 点C += ( 点R0 * 影響度0 ) + ( 点R1 * 影響度1 ); // 膨らみすぎ防止 * * 点P -= 点C; // 頂点を点Cが中心になるよう移動して * 点P = Vector4.Transform( 点P, 重み付き回転行列 ); // 回転して * 点P += 点C; // 元の位置へ * * スキニング後の入力頂点リスト[ i ].Position = 点P; * スキニング後の入力頂点リスト[ i ].Normal = Vector3.TransformNormal( 頂点.Normal, 重み付き回転行列 ); * スキニング後の入力頂点リスト[ i ].Normal.Normalize(); * } * //---------------- #endregion * break; * * case (uint) 変形方式.QDEF: #region " *** " * //---------------- * { * // ※ QDEFを使ったモデルが見つからないのでテストしてません。あれば教えてください! * * var 頂点 = 入力頂点リスト[ i ]; * * var dualQuaternion = new DualQuaternion[ 4 ]; // 最大4ボーンまで対応 * * var boneIndexes = new[] { 頂点.BoneIndex1, 頂点.BoneIndex2, 頂点.BoneIndex3, 頂点.BoneIndex4 }; * var boneWeights = new[] { 頂点.BoneWeight1, 頂点.BoneWeight2, 頂点.BoneWeight3, 頂点.BoneWeight4 }; * * for( int b = 0; b < 4; b++ ) * { * if( boneWeights[ b ] == 0f ) * { * dualQuaternion[ b ] = DualQuaternion.Zero; // 未使用 * } * else * { * dualQuaternion[ b ] = new DualQuaternion( boneTrans[ boneIndexes[ b ] ] ); * } * } * * Matrix bt = ( * dualQuaternion[ 0 ] * boneWeights[ 0 ] + * dualQuaternion[ 1 ] * boneWeights[ 1 ] + * dualQuaternion[ 2 ] * boneWeights[ 2 ] + * dualQuaternion[ 3 ] * boneWeights[ 3 ] ).ToMatrix(); * * if( Matrix.Zero == bt ) * bt = Matrix.Identity; * * スキニング後の入力頂点リスト[ i ].Position = Vector4.Transform( 頂点.Position, bt ); * スキニング後の入力頂点リスト[ i ].Normal = 頂点.Normal; * } * //---------------- #endregion * break; * } * * スキニング後の入力頂点リスト[ i ].UV = 入力頂点リスト[ i ].UV; * スキニング後の入力頂点リスト[ i ].AddUV1 = 入力頂点リスト[ i ].AddUV1; * スキニング後の入力頂点リスト[ i ].AddUV2 = 入力頂点リスト[ i ].AddUV2; * スキニング後の入力頂点リスト[ i ].AddUV3 = 入力頂点リスト[ i ].AddUV3; * スキニング後の入力頂点リスト[ i ].AddUV4 = 入力頂点リスト[ i ].AddUV4; * スキニング後の入力頂点リスト[ i ].EdgeWeight = 入力頂点リスト[ i ].EdgeWeight; * スキニング後の入力頂点リスト[ i ].Index = 入力頂点リスト[ i ].Index; * } * * // データストリームに、スキニング後の入力頂点リストを書き込む。 * _頂点データストリーム.WriteRange( スキニング後の入力頂点リスト ); * _頂点データストリーム.Position = 0; * * // D3D頂点バッファに、スキニング後の入力頂点リストを(データストリーム経由で)書き込む。 * RenderContext.Instance.DeviceManager.D3DDeviceContext.UpdateSubresource( new DataBox( _頂点データストリーム.DataPointer, 0, 0 ), D3D頂点バッファ, 0 ); */ //---------------- #endregion D3Dスキニングバッファをリセットする = false; }