public void ボーンを指定したフレームの姿勢に更新する(float 目標フレーム) { // 目標フレームの前後のキーフレームを探す _frameManager.現在のフレームの前後のキーフレームを探して返す(目標フレーム, out MMDFileParser.IFrameData 過去フレーム, out MMDFileParser.IFrameData 未来フレーム); var 過去のボーンフレーム = (ボーンフレーム)過去フレーム; var 未来のボーンフレーム = (ボーンフレーム)未来フレーム; // 目標フレームの前後キーフレーム間での進行度を求めてペジェ関数で変換する float 進行度合0to1 = (未来のボーンフレーム.フレーム番号 == 過去のボーンフレーム.フレーム番号) ? 0 : (float)(目標フレーム - 過去のボーンフレーム.フレーム番号) / (float)(未来のボーンフレーム.フレーム番号 - 過去のボーンフレーム.フレーム番号); // リニア var 移行度合 = new float[4]; for (int i = 0; i < 4; i++) { 移行度合[i] = 過去のボーンフレーム.ベジェ曲線[i].横位置Pxに対応する縦位置Pyを返す(進行度合0to1); // リニア → ベジェ[4] } // ボーンを更新する _ボーン.移動 = CGHelper.ComplementTranslate(過去のボーンフレーム, 未来のボーンフレーム, new Vector3(移行度合[0], 移行度合[1], 移行度合[2])); _ボーン.回転 = CGHelper.ComplementRotateQuaternion(過去のボーンフレーム, 未来のボーンフレーム, 移行度合[3]); }
/// <summary> /// ボーンを指定したフレーム番号の姿勢に更新する /// </summary> /// <param name="frameNumber">フレーム番号</param> public void ReviseBone(ulong frameNumber) { // 現在のフレームの前後のキーフレームを探す MMDFileParser.IFrameData pastFrame, futureFrame; frameManager.現在のフレームの前後のキーフレームを探して返す(frameNumber, out pastFrame, out futureFrame); var pastBoneFrame = (BoneFrame)pastFrame; var futureBoneFrame = (BoneFrame)futureFrame; // 現在のフレームの前後キーフレーム間での進行度を求めてペジェ関数で変換する float s = (pastBoneFrame.frameNumber == futureBoneFrame.frameNumber) ? 0 : (float)(frameNumber - pastBoneFrame.frameNumber) / (float)(futureBoneFrame.frameNumber - pastBoneFrame.frameNumber); // 進行度 BezInterpolParams p = pastBoneFrame.interpolParameters; float s_X, s_Y, s_Z, s_R; if (p != null) { s_X = BezEvaluate(p.X1, p.X2, s); s_Y = BezEvaluate(p.Y1, p.Y2, s); s_Z = BezEvaluate(p.Z1, p.Z2, s); s_R = BezEvaluate(p.R1, p.R2, s); // ペジェ変換後の進行度 } else { //ベジェ曲線のパラメータがないときは線形補完の量としてsを利用する s_X = s_Y = s_Z = s_R = s; } // ボーンを更新する bone.移動 = new SharpDX.Vector3( CGHelper.Lerp(pastBoneFrame.position.x, futureBoneFrame.position.x, s_X), CGHelper.Lerp(pastBoneFrame.position.y, futureBoneFrame.position.y, s_Y), CGHelper.Lerp(pastBoneFrame.position.z, futureBoneFrame.position.z, s_Z)); bone.回転 = SharpDX.Quaternion.Slerp(pastBoneFrame.rotation.ToSharpDX(), futureBoneFrame.rotation.ToSharpDX(), s_R); }
public float 指定したフレームにおけるモーフ値を取得する(float フレーム) { // 現在のフレームの前後のキーフレームを探す MMDFileParser.IFrameData 前フレーム, 後フレーム; _frameManager.現在のフレームの前後のキーフレームを探して返す(フレーム, out 前フレーム, out 後フレーム); var pastMorphFrame = (モーフフレーム)前フレーム; var futureMorphFrame = (モーフフレーム)後フレーム; // 現在のフレームの前後キーフレーム間での進行度を求めてペジェ関数で変換する float s = (futureMorphFrame.フレーム番号 == pastMorphFrame.フレーム番号) ? 0 : (float)(フレーム - pastMorphFrame.フレーム番号) / (float)(futureMorphFrame.フレーム番号 - pastMorphFrame.フレーム番号); // 進行度 return(CGHelper.Lerp(pastMorphFrame.モーフ値, futureMorphFrame.モーフ値, s)); }
public float 指定したフレームにおけるモーフ値を取得する(ulong フレーム) { // 現在のフレームの前後のキーフレームを探す MMDFileParser.IFrameData 前フレーム, 後フレーム; _frameManager.現在のフレームの前後のキーフレームを探して返す(フレーム, out 前フレーム, out 後フレーム); var pastMorphFrame = (MorphFrame)前フレーム; var futureMorphFrame = (MorphFrame)後フレーム; // 現在のフレームの前後キーフレーム間での進行度を求める float s = (futureMorphFrame.frameNumber == pastMorphFrame.frameNumber) ? 0 : (float)(フレーム - pastMorphFrame.frameNumber) / (float)(futureMorphFrame.frameNumber - pastMorphFrame.frameNumber); // 進行度 // 線形補完で値を求める return(CGHelper.Lerp(pastMorphFrame.value, futureMorphFrame.value, s)); }
private void _フレームを更新する(カメラフレーム cameraFrame1, カメラフレーム cameraFrame2, カメラ camera, 射影 projection, float 進行度合い0to1) { float ProgX = cameraFrame1.ベジェ曲線[0].横位置Pxに対応する縦位置Pyを返す(進行度合い0to1); float ProgY = cameraFrame1.ベジェ曲線[1].横位置Pxに対応する縦位置Pyを返す(進行度合い0to1); float ProgZ = cameraFrame1.ベジェ曲線[2].横位置Pxに対応する縦位置Pyを返す(進行度合い0to1); float ProgR = cameraFrame1.ベジェ曲線[3].横位置Pxに対応する縦位置Pyを返す(進行度合い0to1); float ProgL = cameraFrame1.ベジェ曲線[4].横位置Pxに対応する縦位置Pyを返す(進行度合い0to1); float ProgP = cameraFrame1.ベジェ曲線[5].横位置Pxに対応する縦位置Pyを返す(進行度合い0to1); // カメラ(ビュー) camera.移動する( 注視点からの距離: CGHelper.Lerp(cameraFrame1.距離, cameraFrame2.距離, ProgL), 注視点の位置: CGHelper.ComplementTranslate(cameraFrame1, cameraFrame2, new Vector3(ProgX, ProgY, ProgZ)), 回転: CGHelper.ComplementRotateQuaternion(cameraFrame1, cameraFrame2, ProgR)); // 射影 float angle = CGHelper.Lerp(cameraFrame1.視野角, cameraFrame2.視野角, ProgP); projection.視野角rad = CGHelper.ToRadians(angle); }
private bool _一回のIK演算を実行する(PMXボーン IKbone) { var エフェクタ = IKbone.IKターゲットボーン; var ターゲット = IKbone; // すべての IKリンク について…… foreach (var ikLink in IKbone.IKリンクリスト) { var IKリンクのローカル座標へ変換する行列 = Matrix.Invert(ikLink.IKリンクボーン.モデルポーズ行列); var IKリンクローカル座標でのエフェクタ位置 = Vector3.TransformCoordinate(エフェクタ.ローカル位置, エフェクタ.モデルポーズ行列 * IKリンクのローカル座標へ変換する行列); var IKリンクローカル座標でのターゲット位置 = Vector3.TransformCoordinate(ターゲット.ローカル位置, ターゲット.モデルポーズ行列 * IKリンクのローカル座標へ変換する行列); var V1 = Vector3.Normalize(IKリンクローカル座標でのエフェクタ位置 - ikLink.IKリンクボーン.ローカル位置); var V2 = Vector3.Normalize(IKリンクローカル座標でのターゲット位置 - ikLink.IKリンクボーン.ローカル位置); // IKリンクを更新する ikLink.現在のループ回数++; // 回転軸 Vaxis = v1×v2 を計算する。 var Vaxis = Vector3.Cross(V1, V2); // 回転軸周りの回転角 θa = Cos-1(V1・V2) を計算する。 var 内積 = (double)Vector3.Dot(V1, V2); 内積 = Math.Max(Math.Min(内積, 1.0), -1.0); // 誤差丸め double θa = Math.Acos(内積); // 内積: -1 → 1 のとき、θa: π → 0 θa = Math.Min(θa, IKbone.IK単位角); // θa は単位角を超えないこと if (θa <= 0.00001) { continue; // θa が小さすぎたら無視 } // Vaxis と θa から、回転クォータニオンを計算する。 var 回転クォータニオン = Quaternion.RotationAxis(Vaxis, (float)θa); 回転クォータニオン.Normalize(); // IKリンクに回転クォータニオンを適用する。 ikLink.IKリンクボーン.回転 *= 回転クォータニオン; // 回転量制限があれば適用する。 if (ikLink.回転制限がある) { #region " 回転量制限 " //---------------- float X軸回転角, Y軸回転角, Z軸回転角; // 回転にはクォータニオンを使っているが、PMXのボーンの最小回転量・最大回転量は、クォータニオンではなくオイラー角(X,Y,Z)で指定される。 // そのため、回転数制限の手順は // (1) クォータニオンをいったんオイラー角(X,Y,Z)に変換 // (2) X,Y,Z に対して回転量制限チェック // (3) オイラー角を再びクォータニオンに戻す // となる。 // // また、オイラー角で回転を表す場合、各軸の回転の順番が重要となる。 // ここでは、 // (A) X → Y → Z の順 // (B) Y → Z → X の順 // (C) Z → X → Y の順 // の3通りを調べて、ジンバルロックが発生しない最初の分解値を採用する。 if (CGHelper.クォータニオンをXYZ回転に分解する(ikLink.IKリンクボーン.回転, out X軸回転角, out Y軸回転角, out Z軸回転角)) // ジンバルロックが発生しなければ true { // (A) XYZ 回転 var clamped = Vector3.Clamp( new Vector3(X軸回転角, Y軸回転角, Z軸回転角).オイラー角の値域を正規化する(), ikLink.最小回転量, ikLink.最大回転量); X軸回転角 = clamped.X; Y軸回転角 = clamped.Y; Z軸回転角 = clamped.Z; ikLink.IKリンクボーン.回転 = Quaternion.RotationMatrix(Matrix.RotationX(X軸回転角) * Matrix.RotationY(Y軸回転角) * Matrix.RotationZ(Z軸回転角)); // X, Y, Z の順 } else if (CGHelper.クォータニオンをYZX回転に分解する(ikLink.IKリンクボーン.回転, out Y軸回転角, out Z軸回転角, out X軸回転角)) // ジンバルロックが発生しなければ true { // (B) YZX 回転 var clamped = Vector3.Clamp( new Vector3(X軸回転角, Y軸回転角, Z軸回転角).オイラー角の値域を正規化する(), ikLink.最小回転量, ikLink.最大回転量); X軸回転角 = clamped.X; Y軸回転角 = clamped.Y; Z軸回転角 = clamped.Z; ikLink.IKリンクボーン.回転 = Quaternion.RotationMatrix(Matrix.RotationY(Y軸回転角) * Matrix.RotationZ(Z軸回転角) * Matrix.RotationX(X軸回転角)); // Y, Z, X の順 } else if (CGHelper.クォータニオンをZXY回転に分解する(ikLink.IKリンクボーン.回転, out Z軸回転角, out X軸回転角, out Y軸回転角)) // ジンバルロックが発生しなければ true { // (C) ZXY 回転 var clamped = Vector3.Clamp( new Vector3(X軸回転角, Y軸回転角, Z軸回転角).オイラー角の値域を正規化する(), ikLink.最小回転量, ikLink.最大回転量); X軸回転角 = clamped.X; Y軸回転角 = clamped.Y; Z軸回転角 = clamped.Z; //ikLink.ikLinkBone.回転行列 = Quaternion.RotationYawPitchRoll( Y軸回転角, X軸回転角, Z軸回転角 ); ikLink.IKリンクボーン.回転 = Quaternion.RotationMatrix(Matrix.RotationZ(Z軸回転角) * Matrix.RotationX(X軸回転角) * Matrix.RotationY(Y軸回転角)); // Z, X, Y の順 } else { // その他はエラー continue; } //---------------- #endregion } // IKリンクの新しい回転行列を反映。 ikLink.IKリンクボーン.モデルポーズを更新する(); } return(true); }
/// <summary> /// 現時点までに蓄積された三角形や四角形から、インデックスバッファを生成して返す。 /// </summary> public Buffer インデックスバッファを作成する() { return(CGHelper.D3Dバッファを作成する(_IndexList, RenderContext.Instance.DeviceManager.D3DDevice, BindFlags.IndexBuffer)); }