/// <summary> /// IKのソルブ /// </summary> /// <param name="ik">対象IK</param> /// <param name="BoneManager">ボーンマネージャ</param> /// <returns>呼び出し側でUpdateGlobalをもう一度呼ぶ場合はtrue</returns> public bool Solve(MMDIK ik, MMDBoneManager BoneManager) { #if SlimDX Vector4 localTargetPos = Vector4.Zero; Vector4 localEffectorPos = Vector4.Zero; #else Vector3 localTargetPos = Vector3.Zero; Vector3 localEffectorPos = Vector3.Zero; #endif //エフェクタとなるボーンを取得 MMDBone effector = ik.IKTargetBone; //IK対象のボーンのGlobalを更新(別のIK影響下のボーンからIKチェインが出ている場合があるので) Matrix local; for (int i = ik.IKChildBones.Count - 1; i >= 0; --i) {//順番に親子関係になっている。(Processorでチェックかけてある //GlobalTransformを仮更新 int parentBone = ik.IKChildBones[i].SkeletonHierarchy; ik.IKChildBones[i].LocalTransform.CreateMatrix(out local); Matrix.Multiply(ref local, ref BoneManager[parentBone].GlobalTransform, out ik.IKChildBones[i].GlobalTransform); } effector.LocalTransform.CreateMatrix(out local); Matrix.Multiply(ref local, ref BoneManager[effector.SkeletonHierarchy].GlobalTransform, out effector.GlobalTransform); //ターゲット位置の取得 Vector3 targetPos; Matrix.GetTranslation(ref ik.IKBone.GlobalTransform, out targetPos); //最大ループ回数分ループ for (int it = 0; it < ik.Iteration; ++it) { for (int nodeIndex = 0; nodeIndex < ik.IKChildBones.Count; ++nodeIndex) {//子ノードを子から順番に…… MMDBone node = ik.IKChildBones[nodeIndex]; //エフェクタの位置 Vector3 effectorPos; Matrix.GetTranslation(ref effector.GlobalTransform, out effectorPos); // 注目ノードの位置の取得 Vector3 jointPos; Matrix.GetTranslation(ref node.GlobalTransform, out jointPos); // ワールド座標系から注目ノードの局所座標系への変換 Matrix invCoord; Matrix.Invert(ref node.GlobalTransform, out invCoord); // 各ベクトルの座標変換を行い、検索中のボーンi基準の座標系にする // (1) 注目ノード→エフェクタ位置へのベクトル(a)(注目ノード) Vector3.Transform(ref effectorPos, ref invCoord, out localEffectorPos); // (2) 基準関節i→目標位置へのベクトル(b)(ボーンi基準座標系) Vector3.Transform(ref targetPos, ref invCoord, out localTargetPos); #if SlimDX //念のため…… // (1) 基準関節→エフェクタ位置への方向ベクトル Vector3 basis2Effector = Vector3.Normalize(new Vector3(localEffectorPos.X, localEffectorPos.Y, localEffectorPos.Z)); // (2) 基準関節→目標位置への方向ベクトル Vector3 basis2Target = Vector3.Normalize(new Vector3(localTargetPos.X, localTargetPos.Y, localTargetPos.Z)); #else // (1) 基準関節→エフェクタ位置への方向ベクトル Vector3 basis2Effector = Vector3.Normalize(localEffectorPos); // (2) 基準関節→目標位置への方向ベクトル Vector3 basis2Target = Vector3.Normalize(localTargetPos); #endif // 回転角 float rotationDotProduct = (float)Vector3.Dot(basis2Effector, basis2Target); float rotationAngle = (float)Math.Acos(rotationDotProduct); //回転量制限をかける if (rotationAngle > MathHelper.Pi * ik.ControlWeight * (nodeIndex + 1)) { rotationAngle = MathHelper.Pi * ik.ControlWeight * (nodeIndex + 1); } if (rotationAngle < -MathHelper.Pi * ik.ControlWeight * (nodeIndex + 1)) { rotationAngle = -MathHelper.Pi * ik.ControlWeight * (nodeIndex + 1); } // 回転軸 Vector3 rotationAxis = Vector3.Cross(basis2Effector, basis2Target); BoneManager.IKLimitter.Adjust(node.Name, ref rotationAxis); rotationAxis.Normalize(); if (!float.IsNaN(rotationAngle) && rotationAngle > 1.0e-3f && !rotationAxis.NaN) { // 関節回転量の補正 Quaternion subRot = Quaternion.CreateFromAxisAngle(rotationAxis, (decimal)rotationAngle); Quaternion.Multiply(ref subRot, ref node.LocalTransform.Rotation, out node.LocalTransform.Rotation); BoneManager.IKLimitter.Adjust(node); //関係ノードのグローバル座標更新 for (int i = nodeIndex; i >= 0; --i) {//順番に親子関係になっている。(Processorでチェックかけてある //GlobalTransformを仮更新 int parentBone = ik.IKChildBones[i].SkeletonHierarchy; ik.IKChildBones[i].LocalTransform.CreateMatrix(out local); Matrix.Multiply(ref local, ref BoneManager[parentBone].GlobalTransform, out ik.IKChildBones[i].GlobalTransform); } effector.LocalTransform.CreateMatrix(out local); Matrix.Multiply(ref local, ref BoneManager[effector.SkeletonHierarchy].GlobalTransform, out effector.GlobalTransform); } } } return(true);//UpdateGlobalをもう一度呼ぶ //IKチェインにぶら下がってるIK影響外のボーンを更新するため。 }
public MMDModel Load(string filename, Dictionary<string, object> opaqueData) { //GraphicsDataのチェック if (!opaqueData.ContainsKey("GraphicsDevice")) throw new ArgumentException("opaqueデータに{\"GraphicsDevice\",GraphicsDevice}がありません", "opaqueData"); GraphicsDevice graphics = opaqueData["GraphicsDevice"] as GraphicsDevice; if (graphics == null) throw new ArgumentException("opaqueデータに\"GraphicsDevice\"のキーはありましたが、ValueにGraphicsDeviceが入っていません"); //PMDのロード MikuMikuDance.Model.MMDModel model = MikuMikuDance.Model.ModelManager.Read(filename); MikuMikuDance.Model.Ver1.MMDModel1 model1 = model as MikuMikuDance.Model.Ver1.MMDModel1; if (model1 == null) throw new System.IO.FileLoadException("ファイルのロードに失敗しました", filename); List<IMMDModelPart> parts = new List<IMMDModelPart>(); MMDBoneManager manager; Dictionary<string, MMDMotionData> attachedMotion = new Dictionary<string, MMDMotionData>(); long FaceIndex = 0; //ボーンマネージャの作成 MMDBone[] bones = new MMDBone[model1.Bones.Length]; for (int i = 0; i < bones.Length; ++i) { bones[i].BindPose } //マテリアルごとにモデルを作成 for (long i = 0; i < model1.Materials.LongLength; ++i) { List<ushort> vertIndices = new List<ushort>(); List<int> faceIndices = new List<int>(); Dictionary<ushort, int> vertMap = new Dictionary<ushort, int>(); //頂点インデックスの作成 for (long j = FaceIndex; j < FaceIndex + model1.Materials[i].FaceVertCount; ++j) { ushort VertIndex = model1.FaceVertexes[j]; int vpos; if (vertMap.TryGetValue(VertIndex, out vpos)) { faceIndices.Add(vpos); } else { vertMap.Add(VertIndex, vertIndices.Count); faceIndices.Add(vertIndices.Count); vertIndices.Add(VertIndex); } } //インデックスバッファの作成(GraphicsDeviceがいる。どうする?) IndexBuffer indexbuffer; //頂点の作成 MMDVertexNm[] verts; MMDVertexNmTx[] vertsTx = null; //頂点型判定と代入 if (!string.IsNullOrEmpty(model1.Materials[i].TextureFileName)) { vertsTx = new MMDVertexNmTx[vertIndices.Count]; verts = vertsTx; } else verts = new MMDVertexNm[vertIndices.Count]; for (int vi = 0; vi < verts.LongLength; ++vi) { MikuMikuDance.Model.Ver1.ModelVertex modelvert = model1.Vertexes[vertIndices[vi]]; verts[vi].Position = MMDXMath.ToVector3(modelvert.Pos); verts[vi].Normal = MMDXMath.ToVector3(modelvert.NormalVector); verts[vi].BlendWeights = new Microsoft.Xna.Framework.Vector2(modelvert.BoneWeight / 100f, 1.0f - modelvert.BoneWeight / 100f); verts[vi].BlendIndexX = modelvert.BoneNum[0]; verts[vi].BlendIndexY = modelvert.BoneNum[1]; if (verts[vi].BlendIndexX < 0 && verts[vi].BlendIndexX >= manager.Count) { verts[vi].BlendWeights.X = 0; verts[vi].BlendIndexX = 0; } if (verts[vi].BlendIndexY < 0 && verts[vi].BlendIndexY >= manager.Count) { verts[vi].BlendWeights.Y = 0; verts[vi].BlendIndexY = 0; } if (vertsTx != null) vertsTx[vi].TextureCoordinate = MMDXMath.ToVector2(modelvert.UV); } //不透明データを作成 Dictionary<string, object> opaqueData = new Dictionary<string, object>(); opaqueData.Add("IndexBuffer", indexbuffer); parts.Add(normalFactory.Create(faceIndices.Count / 3, verts, opaqueData)); } return new MMDModel(parts, manager, attachedMotion); }