2011年4月28日木曜日

お知らせ

引越しのため、暫くブログを休止します。
具体的な復帰時期は未定です。

もし見てくださっている方々が居たのなら、 ご迷惑をおかけしますがご了承下さい。

何かしらのご要望があれば、この記事のコメントに書いてください。
不具合等のご連絡、是非お願いします。

2011年4月25日月曜日

Blenderのボーンの仕様【ウェイト崩壊の原因について】

前回のBlenderでMMDのボーン読み込みで一部ウェイトが適応されなかった理由がわかりました。

Blenderのコンソール画面で以下の注意書きが表示されていました。

文字化けしてますが、それぞれ「左腕捩1」,「左腕捩2」,「左腕捩3」,「右腕捩1」,「右腕捩2」,「右腕捩3」という名前のボーンで、注意書きの内容は「サイズが0のボーンを取り除きました」ということらしいです。

PMDViewでこの6つのボーンを調べてみました。


調べた結果、ボーンの座標が子ボーンの座標と同じ位置にありました。

Blenderの仕様で長さが0のボーンは消す仕組みになってるらしく、
注意書きが出たこの6つのボーンは全て長さが0だったため排除されたようです。

頂点群にこの6つのボーンに影響を受ける頂点が存在したらしく、ボーンが消されてしまった為に前回のような現象に陥ってしまったようです。

解決策は、Blenderで読み込む前にモデル自体をいじって長さが0のボーンを修正してもらうしかないと思います。
下手にこっちがいじって変になったら困りますからね。

PMDViewでボーンを編集してみました。腕のウェイトがちゃんと設定されているのがわかると思います。
video

とまあ、ひとまず解決したのでBlender2.57版も作っていこうと思います。

追記
Blenderの警告画面だと文字化けしてわからないので、新たに警告画面を表示するように更新しました。

2011年4月22日金曜日

Blender2.49用MMDインポーター更新【ボーン読み込みにほぼ対応】

今更ですが、Blender2.49でPMDフォーマットを読み込むスクリプトを更新しました。
XNAでMMD読み込みやったんで以前より仕組みがわかるようになりました。

ようやくボーン読み込みにほぼ対応しました。
ちなみにあえて「ほぼ」といっている理由は、一部のモデルでボーンウェイトが正しく反映できてないからです。

以下は七葉1052式のミクさん。右腕及び左腕のボーンウェイトが一部崩壊してます。

video

腕の部分だけなぜか崩壊します。他のモデルでは問題ないのに。
原因を解明しだいまた更新する予定です。
また、Blender2.57版も作る予定です。

以下は成功例。ボーンは気にしないで下さい。

video


これやったおかげでIKの仕組みを多少理解できました。
XNAに活かせるかな?

2011年4月20日水曜日

XNAでMMD読み込み【アニメーションの実装 Part5】

ニコニコ動画に投稿しながら「ちょっとおかしいな?」と思ったことがありました。
補間処理かけてる筈なのにモーションがやたらカクカク動いてしまうんです。

なので今回はIKを後回しにしてフレーム補間部分を修正することにしました。

以下はSKinningSampleのAnimationPlayer.UpdateBoneTransformsの一部。
いじる前の状態はこんな感じです。
// キーフレームの行列を読み出す
IList<Keyframe> keyframes = currentClipValue.Keyframes;

while (currentKeyframe < keyframes.Count)
{
    Keyframe keyframe = keyframes[currentKeyframe];

    // 現時刻まで到達したら読み込み終了
    if (keyframe.Time > currentTimeValue)
        break;

    // このキーフレームを使う
    boneTransforms[keyframe.Bone] = keyframe.Transform;

    currentKeyframe++;
}
キーフレーム補間処理を追加し、さらに今回修正したのが以下のものです。
// キーフレームの行列を読み出す
IList<keyframe>keyframes = currentClipValue.Keyframes;
            
while (currentKeyframe < keyframes.Count)
{
    Keyframe keyframe = keyframes[currentKeyframe];

    // 現時刻まで到達したら読み込み終了
    if (keyframe.Time > currentTimeValue)
        break;

    // このキーフレームを使う
    currentKeyframes[keyframe.Bone] = keyframe;
    boneTransforms[keyframe.Bone] = keyframe.Transform;

    currentKeyframe++;
}

// 変換行列作成
foreach(Keyframe currentKey in currentKeyframes)
{
    // 補間作業
    if (currentKey != null && currentKey.NextBone != -1)
    {
        // 自分のボーンの次のキーフレーム情報を取得
        Keyframe nextKey = keyframes[currentKey.NextBone];

        // 現在のフレーム位置を算出
        float weight = (float)((currentTimeValue - currentKey.Time).TotalSeconds / (nextKey.Time - currentKey.Time).TotalSeconds);

        // ベジェ曲線補間
        boneTransforms[currentKey.Bone].Translation.X = MathHelper.Lerp(currentKey.Transform.Translation.X, nextKey.Transform.Translation.X, nextKey.Bezier.AxisX.GetRate(weight)); // X軸移動補間

        boneTransforms[currentKey.Bone].Translation.Y = MathHelper.Lerp(currentKey.Transform.Translation.Y, nextKey.Transform.Translation.Y, nextKey.Bezier.AxisY.GetRate(weight)); // Y軸移動補間

        boneTransforms[currentKey.Bone].Translation.Z = MathHelper.Lerp(currentKey.Transform.Translation.Z, nextKey.Transform.Translation.Z, nextKey.Bezier.AxisZ.GetRate(weight)); // Z軸移動補間

        boneTransforms[currentKey.Bone].Rotation = Quaternion.Slerp(currentKey.Transform.Rotation, nextKey.Transform.Rotation, nextKey.Bezier.Rotation.GetRate(weight)); // 回転補間
    }
}
AnimationPlayerクラスに新たなメンバを追加。
Keyframe[] currentKeyframes; // 補間対象ボーンの現在のキーフレーム情報
Keyframeクラスに新たなメンバを追加。
BezierParams bezierValue; // ベジェ曲線
public int NextBone { get; set; } // 次のキーフレーム情報が格納されてる配列のインデックス
修正する前のソースがあればよかったんですけど、保存し忘れました。
まぁカクカクしてた理由は簡単に説明すると、「書き換えてはいけない行列を更新してしまったから」というところでしょうか?

修正後に実際にXNAで動作させたものです。
前回ニコニコ動画に投稿したものと比べ、動きが滑らかになっているのがわかると思います。

video

今回は以上です。
そろそろバックアップもとっていかないといけないですね。
保守もありますけど、比較もかねて

2011年4月18日月曜日

XNAでMMD読み込み【アニメーションの実装 Part4】

前回の修正。
一部のモーションが崩壊してた理由が判明。単純にキーフレーム行列を座標系変換し忘れてただけでした。
Z軸移動とX・Y軸回転を修正したら(前回と比べると)まともに動くようになりました。

あとオリジナルフォーマットの仕様を少し修正。
<?xml version="1.0" encoding="shift-jis" ?>
<root>
    <Model name="model" source="初音ミク-1052C-Re ノーマルEHs1.pmd" />
    <Motions>
        <Motion name="移動" interpolation="true">move.vmd</Motion>
        <Motion name="回転" interpolation="true">rotate.vmd</Motion>
    <Motion name="合成" interpolation="true">test.vmd</Motion>
    <Motion name="腕振り" interpolation="true">motion.vmd</Motion>
    <Motion name="踊り" interpolation="false">nyanyanya.vmd</Motion>
    </Motions>
</root> 
各タグ名前要素は必ず書くようにして、Motionタグにinterpolation要素を追加。
コンパイル時にキーフレーム補間してベイクするかを判断します。

video

あとは補間をSkinningSampleで行い、IK計算を行えばまともに動くのかな?

あと今回、pmdファイルだけ読み込む拡張パイプラインを倉庫に追加しました。
モーションをつけないモデルやマップ等にお使い下さい。表情は変えることは出来ません。

今回はここまで。次はぼちぼちIKボーンの計算をやろうと思います。

XNAでMMD読み込み【アニメーションの実装 Part3】

今回はvmdファイルの読み込みをコンテンツパイプラインで行うように修正しました。
とりあえず、モデルとモーションを同時に読み込ませる為、以下のようなフォーマットを作成してパイプラインに読み込ませるようにしました。
<?xml version="1.0" encoding="shift-jis" ?>
<root>
 <model source="初音ミク-1052C-Re ノーマルEHs1.pmd" />
 <motions>
  <motion name="踊り">nyanyanya.vmd</motion>
 </motions>
</root>
<Model>、<Motion>共にname属性とsource属性を持つことが出来ます。
sourceの値はタグで囲むことで省略可、nameを省略するとsource要素と同じ値になるようにしました(拡張子は省く)。

実際に読み込とこのようにになります。
/// <summary>
/// LoadContent はゲームごとに 1 回呼び出され、ここですべてのコンテンツを
/// 読み込みます。
/// </summary>
protected override void LoadContent()
{
        // 新規の SpriteBatch を作成します。これはテクスチャーの描画に使用できます。
        spriteBatch = new SpriteBatch(this.GraphicsDevice);
        font = Content.Load<spritefont>("メイリオ");
        Line.Initialize(graphics);

        // TODO: this.Content クラスを使用して、ゲームのコンテンツを読み込みます。

        // フォーマットのファイル名を指定 ここから<Model>タグと<Motion>タグで指定したファイルを読み込む
        model = Content.Load<model>("model"); 

        // スキン情報をモデルのTagから取得
        SkinningData skinningData = model.Tag as SkinningData;

        if (skinningData == null)
                throw new InvalidOperationException
                ("このモデルのTagにはSkinningDataが設定されていません。\n" +
                "プロセッサにSkinnedModelProcessorを指定してください");

        // アニメーションプレイヤーの生成と、アニメーションクリップ再生開始
        animationPlayer = new AnimationPlayer(skinningData);
        animationPlayer.StartClip(skinningData.AnimationClips["踊り"]); // <Motion>タグのnameの値
アニメーションにはSkinningSampleを使用。
当初はQuatSkinningSampleを使う予定でしたが、MMDのボーン数が定数レジスタを遥かに上回ってしまったので急遽TexSkinningSampleに切り替えました。

TexSkinningSampleは頂点テクスチャでアニメーションをさせるライブラリです。
テクスチャにボーン情報を書き込み、シェーダー内でテクスチャから情報を引き出す形式になっているようです。

これによって、VS3.0限定ですが定数レジスタに制限させることなく256個のボーンをフルに使えるようになりました。すばらしいライブラリです。

詳しい説明はコチラ→ひにけにXNA 骨がほしい
配布先→ひにけにXNA 頂点テクスチャでスキンアニメーション

最初QuatSkinningSampleで試してずっとうまくいかなくて悩んでいたんですが、TexSkinningSampleに切り替えることでうまく描画できるようになりました。

以下はXNAで実行した動画です。上はキーフレームをベジェ曲線補間してベイクしたもの、下は補間せずに出力したものです。

video
video

下のモーションもベジェ曲線補間してベイクしたかったんですが、コンパイルに恐ろしく時間が掛かるのでやめました。キーフレーム数が少ないと大丈夫なのですが、配布されてるモーションデータだとコンパイルに20分以上掛かります。全く使い物になりません。

ベジェ曲線補間はあらかじめベイクせず、動的に計算した方がいいようです。
まぁとりあえず動いたんで一応OKといったところですね。あとIKボーンの計算さえ入れればまともな動きをするようになるでしょうか?

2011年4月15日金曜日

Blender2.57がリリースされたのでメタセコイア&MMDのスクリプト2.57版を作ってみた!【追加機能あり】

Blender2.57がリリースされました。なんでも、2.5シリーズ初の安定版だとか。
ようやくBeta版ともおさらばですね。

記事
Blender 2.57ここからダウンロード出来ます

てなわけで、早速2.57用スクリプトを作りました。
以前作ったRC2版を導入しても問題なく動くんですけど、せっかくなので
メタセコイアインポーターに回転体の読み込み処理を追加しておきました。

最新安定版が出たので、これで暫くはBlenderの作業はしなくても済みそうです。
メタセコイアスクリプトは、曲面の実装はしないのであとは接続鏡面の実装くらいでしょうか?
MMDのスクリプトは、XNAが終わってからゆっくりとやっていこうと思ってます。

※注意点
今回メタセコイアインポーターに導入した『回転体の実体化』機能ですが、
マテリアルにテクスチャを使用してる場合、UV座標がうまく展開できない場合があります。UV座標を正しく展開させたい方は、あらかじめメタセコイア側で回転体をフリーズしておいてください。

2011年4月14日木曜日

XNAでMMD読み込み【アニメーションの実装 Part2】

今回はベジェ曲線補間を行って描画してみました。
前回と比べて滑らかな動きが出来るようになってます。

上がMMDでのモーション実行動画、下がXNAでの実行動画です。

video
video

また、前回XNAの方のアニメーション速度が速かったので、ボーン更新の時間の処理を修正してみました。同じくらいの速度になっていると思います。MMDって1秒:30フレームなんですね。

ついでなのでFPSを表示させました。およそ53~60の範囲で動いています。
思っていたより処理が軽かったです。
補間計算をあらかじめパイプラインでやらせ、描画周りをGPUに任せればもっと速くなりそうです。

XNAでMMD読み込み【アニメーションの実装 Part1】

先日の続き。アニメーションをさせます。

パイプラインからMMDモデルを読み込んだ後、Gameクラス内でモーションファイルを読み込んでみました。
まだモーションのインポートに関してはパイプラインへの導入は行っていません。

とりあえずテストの為に、以下のようなモーションデータを作ってみました。
両腕をZ軸回転、センターボーンをY軸移動させました。

video

実際にXNAで動かしたものがコチラです。
まだモデルは動きませんが、ボーンだけ動かせるようになりました。
因みにIKの計算はしてません。キーフレーム補間も単純な線形補間しかしてません。

video

なんとかうまく動くようになりました。あとはコレをパイプラインへ導入すれば何とか形になりそうですね。他のモーションデータも入手してテストする必要もあります。それと補間も修正しておかないと。SkinningSampleの大改造もやらないといけないですね。

今のところ、残ってる問題点はIKボーンの計算かな?
まぁ地道にやっていきます。

2011年4月12日火曜日

【OpenGL】glutとCgfxでメタセコイア描画

先日学生時代の友人とOpenGLの話になり、急遽OpenGLでメタセコイアを描画するコードを書いてみました。
ついでなので、OpenGLでシェーダーを組む方法を学ぶ為にCgfxもやってみました。
DirectXでも使えるらしいので、覚えておいて損はないでしょう。

結果としては、全く汎用性のないメタセコイアビューアーが完成しました。
今現在の状態では『メタセコイアビューアー』というよりも『魔理沙ビューアー』です。


マテリアルはテクスチャのみ反映、ライトは適当なので若干暗いです。
「とりあえず、形状だけだせるならいいや!」って感じですね。

シェーダーはこんな感じ。DirectXで使われてるHLSLとさほど違いがないので簡単に書けました。
mat4 View; // カメラ行列
mat4 World; // モデル行列

vec3 light = {0, 0, -50}; // ライトの位置

sampler2D Texture; // 材質のテクスチャ

/**
 * 頂点シェーダー入力データ
**/
struct VertexInputElements
{
 vec4 pos : POSITION0;
 vec3 normal : NORMAL;
 vec2 uv : TEXCOORD0;
};

/**
 * 頂点シェーダー出力データ
**/
struct VertexOutputElements
{
 vec4 pos : POSITION0;
 vec2 uv : TEXCOORD0;
 vec3 normal : TEXCOORD1;
};

/**
 * ピクセルシェーダー入力データ
**/
struct PixelInputElements
{
 vec4 color : COLOR0;
 vec2 uv : TEXCOORD0;
 vec3 normal : TEXCOORD1;
};

VertexOutputElements VS_Shader(VertexInputElements vertex)
{
 VertexOutputElements output = (VertexOutputElements) 0;
 
 output.pos = mul(mul(vertex.pos, World), View);
 vec4 normal = normalize(mul(vec4(vertex.normal, 1), World));
 output.normal = normal.xyz;
 output.uv = vertex.uv;
 
 return output;
}

float4 PS_Shader(PixelInputElements pixel) : COLOR0
{
 // 簡易ランバート?
 pixel.color = vec4(1, 1, 1, 1);
 pixel.color.rgb *= max(0, dot(pixel.normal, normalize(-light))) * tex2D(Texture, pixel.uv);

 return pixel.color;
}

technique Shader
{
 pass p0
 {
  VertexProgram = compile arbvp1 VS_Shader();
  FragmentProgram = compile arbfp1 PS_Shader();
 }
}

さて、ちょいと寄り道しましたがXNAの作業を続けようと思います。

Cgfxの参考サイト
http://sora-prog.at.webry.info/200904/article_6.html
http://asura.iaigiri.com/OpenGL/gl17.html

2011年4月9日土曜日

XNAでMMD読み込み 【MMDのボーンを描画してみた やり直し】

うん。前回のボーン描画やっぱりまちがってた。

前回のfbxモデルですが、ModelクラスからボーンのAbsoluteTransform行列を
引っ張ってきて描画したらうまくいきました。


AbsoluteTransformは、親のAbsoluteTransformに自身のTransformをかけたものだとか。
つまり、AbsoluteTransform=ボーンのワールド行列、Transform=ボーンのローカル姿勢行列ということです。

前回は、Transformにワールド行列を格納してました。コレじゃダメです。
この辺を理解して行列を再計算して再び描画してみたのがコチラです。


MMD、fbx共にボーンを表示することが出来ました。

あと、非表示ボーンを省く処理を消しました。
若干長さが違うような気がしたのは、気のせいではなかったようです。
ボーンの長さも自然になってるとおもいます。

さて、ボーンもうまく読み込めたので、次回からSkinningSampleを使ってアニメーションを実装する作業に入っていこうと思います。

ただ、まだ幾つか問題が残っています。
①IKボーンの計算
②pmd、vmdの読み込み方法
③表情モーションの実装

は、ベジェ曲線補間でモーションをベイクする際に同時にやってしまうつもりですが、計算方法がわからないので調べる必要があります。

は、3種類の方法を考えてます。
A. モデルファイル、モーションファイル名を書き込んだ新たなファイルを用意して、コンテントパイプラインで読み込み、書き込まれてるファイル名を参照する(結局独自フォーマット?)。

B. モデルファイルをコンテントパイプラインで読み込み、中でモデルファイルを同じ名前のモーションファイルを読み込む(Dxlib形式)。

C.モデルファイル、モーションファイルを別々にコンテントパイプラインで読み込み、二つを合わせる(MMD For XNA形式)。

実装するならACBは制限がかかるからパス。
使い勝手がいいのはCかな?かなり大改造が必要だけど。

は、コメントでも書いたけどSkinningModelかModel.Tag内に
表情データを追加する予定。基本ポーズの頂点データはModelクラスから読み取れるから、毎回頂点バッファを書き換えて描画すれば出来ると思う。
やるならシェーダー側かな?


こんなとこですかね?
実装までには、まだまだ時間が掛かりそうです。

XNAでMMDモーションを直接読み込む Part3 【ニュートン近似法と微分】

昨日の続き。色々調べてみた。
自分なりにわかったことを書いてみる。伝わるかな?

まずは『ベジェ曲線』の数式。

rx0 = 0(端点1)
rx3 = 1(端点2)
rx1(方向点1), rx(2)(方向点2)とした時、

px = rx(1) * 3 * t * (1 - t)^2 + rx2 * 3 * (1 - t) * t^2 + t^3
この時のpxがフレーム位置になる。

つまり今回、指定されたフレーム位置pxのときのtの値を求める必要があるわけだが、これを求めるのに『ニュートン近似法』が必要になってくる。

まず『ニュートン法』の公式により、F(x) = 0を求める関数を用意する。
今回指定されたフレーム位置をt(0)として演算を行っていく。
pxt(0)の値に等しくなった時のt(n)の値がほしいので、
『F(x) = 0』 => 『F(t(n)) = px - t(0)』と表す。

次にF(x)tで微分した導関数を求める。
F(x) = {rx(1) * 3t(n)(1 - t(n)) ^ 2 + rx(2) * 3(1 - t(n)) * t(n)^2 + t(n)^3} - t(0)

これを展開していくと、
{rx(1) * (3t(n) - 6t(n)^2 + 3t(n)^3) + rx(2) * (3t(n)^2 - 3t(n)^3) + t(n)^3} - t(0)
となる。

そしてこの数式をt(n)で微分すると、
F'(x) = rx(1) * (3 - 12t(n) + 9t(n)^2) + rx(2) * (6t(n) - 9t(n)^2) + 3t(n)^2
となるわけだ。

これをニュートン法の公式に当てはめる。
t' = t(n) - F( t(n) ) / F'( t(n) )

この時のF( t(n) )の値が限りなく0に近い値になるまで計算を繰り返し、
ry(1) * 3t'(1 - t')^2 + ry(2) * 3(1 - t') * t'^2 + t'^3
とすれば、重みを算出することが出来るというわけだ。

実際にやってみた。まずは変更前のコードで表示したもの。
tをフレーム位置として考えて重みを計算していた。

指定したフレーム位置は0.5だが、実際のxの値は0.5になっていない。


次は編集後。


指定したフレーム位置とxの値が等しくなっているのがわかると思う。

とまあ、vmdの読み込みを始めて3日目。なんとかベジェ曲線の補間処理を理解することが出来た。これで続きが出来そうです。

もし同じようにベジェ曲線の補間で悩んでいる方がいたら、
このブログよりWikipedia等で調べた方がいいですよ。多分これ、意味がわからなくて参考にならないんじゃないだろうか?
まぁ普通科高校出てる人ならおそらく調べなくても理解できてると思いますが・・・

この記事を書きながら、自分でも「何かいてるんだろう?」と訳がわからなくなることが度々あったんですよね。理解できるような文章が書けてるでしょうか?
人に伝えるって難しいですよね。

そもそも、数Aまでの知識しかないのにベクトル・行列・クォータニオンだけ知ってるというのもおかしな話なんですけどね。
まあ今回のことで、微分について多少理化することが出来ました。いい機会だったと思います。

とりあえず今回はここまで。

2011年4月8日金曜日

XNAでMMDモーションを直接読み込む Part2 【ベジェ曲線補間について考えてみた②】

前回のベジェ曲線から重みを算出するやり方について。

『フーレムの位置を0~1の範囲に設定し、其の数値をキーフレームが持ってるベジェ曲線のtにフレーム位置を代入。算出されたY座標の値を重みにしてキーフレーム間を線形補完する』
と考えたが、これだと指定したフレーム位置の重みが正しく得られないですね。

正確には、
『与えられたフレーム位置xの際のtの値を算出。算出したtをベジェ曲線の公式にあてはめて補間の重みyを計算する』
というのが正しいやりかたですね。

さて、考え方はわかったがこれ計算方法どうすればいいんだろう?

フレーム位置X=0.5
rx(0)=0(端点1)
rx(3)=1(端点2)
rx(1)(方向点1), rx(2)(方向点2)とした時、

Px = rx(0) * (1-t)^3 + rx(1) * 3t(1-t)^2 + rx(2) * 3(1-t) * t^2 + rx(3) * t^3
0.5 = rx(1) * 3t(1-t)^2 + rx(2) * 3(1-t) * t^2 + t^3

となるわけだ。
この数式をt=の形に変えればいいと思ったけど、プログラムでそのままやろうと思ったらかなり難しいことになる。

そこでまた色々と調べたら『ニュートン法』というものを見つけた。
反復法によって近似解を導き出すアルゴリズムだとか。
ニュートン法 Wikipedia

ただなぁ、この手の図式を出されてもピンとこないんだよな。Wikipediaの説明も見たけど正直さっぱりわからん。微分も使うらしい。

どこかにもっとわかりやすく説明してるサイトはないものか・・・
いっそ、高校数学の本買おうかな?数2・数3の奴。

2011年4月7日木曜日

XNAでMMDモーションを直接読み込む Part1 【ベジェ曲線補間について考えてみた①】

先日から.vmdファイルの読み込みを始めました。

とりあえずフォーマットの形式を調べて一つずつ読み込んでいき、
『ヘッダ』『フレーム数』『ボーン名』『フレーム番号』『変換行列』と
順調に読み込めたのだが、次のbyte配列で作業が止まってしまった。

64個値があり、どうやら補間に使うもので『ベジェ曲線』を表すのに必要なデータだということがわかった。

あとあと気付いたのだが、MMDを起動すると左下に
『補間曲線操作』なる欄を発見。どうやらこのパラメータらしい。

どこかで曲線に見覚えはあったんですけど、MMDを殆ど起動したことがないので全然気付かなかったというわけですよ。

さて、ベジェ曲線というこの厄介な代物。名前だけは知ってますが、
工業高校で数Aまでしか教わらなかった私。「なんじゃこりゃ?」という状態に陥り、完全に作業が止まってしまいました。

しかしコレを理解しないとモーションの補完作業が出来ないので、ベジェ曲線について色々と調べることになりました。

vmdフォーマットの参考サイト
VMDファイルフォーマット
針金のブログ VMDメモ
MMD走馬灯日記 補間曲線について←ここのおかげでMMDの補間の仕様を把握出来た

ベジェ曲線の参考サイト
ベジェ曲線
ベジェ曲線 Wikipedia
武蔵システム ベジェ曲線
静岡理工科大学 総合情報学部 数値計算 ベジェ曲線

調べていくことで、MMDのベジェ曲線が3次ベジェ曲線というものだとわかった。

3次ベジェ曲線の数式
任意の数値 tを代入した場合の曲線上の点Pointの座標の求め方
(0≦=t≦1)
r(0):端点1
r(1):方向点1
r(2):方向点2
r(3):端点2の場合、
Point = r(0) * (1-t)^3 + 3r(1)t *(1-t)^2 + 3r(2) * t^2 * (1-t) + r(3) * t^3

となるらしい。
コレを元に、ベジェ曲線を求めるプログラムを書いてみた。


MMDと同じように(0, 0), (x1, y1), (x2, y2), (127, 127)の範囲で調整してみた。
何回か試した結果、MMDの曲線図と同じ結果を得ることが出来た。

あとはこれをモーションの補間に組み込むわけだが・・・どうしよう?
表を見る限り X=速度・時間、Y=重み ということはわかるんだけど・・・・・・

今のところ、
『フーレムの位置を0~1の範囲に設定し、其の数値をキーフレームが持ってるベジェ曲線のtにフレーム位置を代入。算出されたY座標の値を重みにしてキーフレーム間を線形補完する』
という感じで考えている。X座標が完璧に無視されてるが、
はたしてこれでいいのだろうか?

MMDで補間の計算方法が載ってる場所があれば一番良いんだけどな・・・
出来れば数式とかわかりやすく書いてるものがいい。
もう少しベジェ曲線の補間について調べる必要がありそうです。

今回はここまで。しばらくベジェ曲線の補間作業が続きそうです。

Blender 2.57 RC2版メタセコイア・MMDスクリプトのリリース

Blender 2.57 RC2が公開されました。
Blenderの公式ページでダウンロードすることが出来ます。

blender.jp
Blender 2.57 RC2が公開

早速Windows32 zip版をダウンロードし前バージョンのスクリプト試してみましたが、
前回に引き続きまたもや使えなくなっていたので急遽新バージョンを作成しました。

2.57 RCを利用する方は新たなバージョンを入手する必要があります。
開発倉庫に上げたのでお手数ですがダウンロードの方お願いします。

メタセコイアスクリプト2.5
MikuMikuDanceインポーター2.5

ところで、RC2って何なんでしょうかね?(笑)
RCって製品版の一歩前の状態らしいですね。そうか、ようやくですか。
早く新しい安定版出ないかな?

追記:
てか、RC2ってことはRC1も出ていたって事ですよね?
全然気付かなかった・・・まぁいいか。

2011年4月5日火曜日

XNAでMMDモデルを直接読み込む Part3 【MMDのボーンを描画してみた】

ボーンが正しく読み取れてるかどうか確認するためにボーンを描画する
プログラムを作りました。ボーン同士を線でつなぎ、間接部分に球モデルを置いた
だけです。



若干長さが違うような気もしますが、大方形状は形成できています。
ところが、fbxファイルを読み込んでボーンを描画したらひどいことになりました。


なんじゃこりゃ

色々とひどい・・・
BoneContentを作成する時のTransform行列の計算方法がおかしいのかな?

以下はImporterのソースコードの一部。
// ルートノードの作成
NodeContent rootNode = new NodeContent();

// MQOモデルの読み込み            
MMDModel model = MMDModel.Load(filename);

// 両面化
model.CreateBothFaces();
            
// マテリアル作成            
MaterialContent[] materials = new MaterialContent[model.Materials.Length];
for (int i = 0; i < materials.Length; i++)
    materials[i] = model.Materials[i].CreateContent(filename, context);

// ボーンコンテンツの作成
if(model.Bones.Length > 0)
{
    Vector3[] ax1 = { Vector3.Right, Vector3.Up, Vector3.Backward }; // 基軸
    Vector3[] ax2 = { Vector3.Backward, Vector3.Right, Vector3.Right }; // 傾きを調べる軸
    float[] rot = new float[3]; // それぞれX・Y・Z軸の傾いてる角度(ラジアン角)

    Vector3 boneAxis, upperAxis, rotateBoneAxis;

    for (int boneID = 0; boneID < model.Bones.Length; boneID++)
    {
        model.Bones[boneID].Node = new BoneContent();
        model.Bones[boneID].Node.Name = model.Bones[boneID].Name;

        // 角度の初期化
        for (int i = 0; i < rot.Length; i++)
            rot[i] = 0;

        // 接続先ボーンがある場合
        if (model.Bones[boneID].TailBone != null)
        {
            // 現在のボーンの傾き
            boneAxis = model.Bones[boneID].TailBone.Head - model.Bones[boneID].Head;
            boneAxis.Normalize();

      // 各軸の傾きを計算する
            for (int i = 0; i < rot.Length; i++)
            {
                // 基軸と比べて傾きの大きさを取得
                float angle = MathHelper.ToRadians(90 - MathHelper.ToDegrees((float)Math.Acos(Vector3.Dot(ax1[i], boneAxis))));

                upperAxis = Vector3.Cross(ax1[i], boneAxis);

                // 軸が同じ向きじゃない場合
                if (upperAxis != Vector3.Zero)
                {
                    // それぞれの基軸と水平になるようにボーンの傾きを回転させる
                    rotateBoneAxis = Vector3.Transform(boneAxis, Matrix.CreateFromAxisAngle(upperAxis, angle));
                    rot[i] = (float) Math.Acos(Vector3.Dot(rotateBoneAxis, ax2[i]));
                }
            }
        }

        // ボーンオフセット行列
        model.Bones[boneID].Node.Transform = Matrix.Invert(Matrix.CreateFromYawPitchRoll(rot[1], rot[0], rot[2]) * Matrix.CreateTranslation(model.Bones[boneID].Head));
    }

  // 親子ツリーの生成
    foreach (MMDBone bone in model.Bones)
    {
        // 非表示ボーン以外のボーンを親ノードに追加
        if (bone.Type != MMDEnum.BoneType.Hidden)
        {
      // 親ボーンノードに追加
            if (bone.ParentBone != null)
                bone.ParentBone.Node.Children.Add(bone.Node);

            // ルートノードに追加
            else
                rootNode.Children.Add(bone.Node);
        }
    }
}
こんなかんじです。
行列の計算でまだ何かやらないといけないのかな?

とりあえずこのままvmdの読み込みを行って、アニメーションする際に今の状態だと
どういうことになるのか確認した後に対策を練ろうと思います。

というわけで次回から.vmdファイルの読み込みを行います

2011年4月4日月曜日

XNAでMMDモデルを直接読み込む Part2

ボーンと表情の読み込みまで終わりました。

あとは表情とボーンの枠名データですが、これは必要ないので読み込みません。
拡張データも・・・必要ないかな?

とりあえず表情が読み込めるようになったので、早速表情を変えてみました。
やってみてわかったんですけど、UV座標を弄るんじゃなく頂点をずらすことで表情を変えてたんですね。



とまぁ、こんな具合です。
ただ、今コレパイプライン側で表情変えてるので、一度変更すると表情が
固定されちゃうんですよね。

vmd読み込むとなると表情も切り替える必要があるので、メッシュ内の頂点を動的に書き換える作業が必要なんですよね。

今はModelMesh.Draw()で描画してるけど、おそらくGraphicsDevice.DrawIndexedPrimitivesとかGraphicsDevice.DrawUserIndexedPrimitivesを使って描画することになるんだろうな・・・。

なるべくModelMesh.Draw()で済ませたいけど・・・仕方ないかな?
となると、シェーダーを書かないといけない・・・・・・作業が増える一方だ。

追記:
というか、アニメーションを実装するなら必然的にシェーダーを
組まないといけないんだよ!どのみち避けては通れない道だったか・・・・・・

BlenderのMMDインポーターにも表情切り替え入れないといけないな・・・
Blender2.57でたら考えよう!

2011年4月3日日曜日

XNAでMMDモデルを直接読み込む Part1

前回のメタセコイアに引き続き、今回はMMDを読み込みました。

読み込む方法やクラス構造などはBlenderやAndroidの時のものを流用。
パイプライン作成はメタセコイアのものを流用。
おかげで前回より作業効率UP!

とりあえずは.pmdファイルを頂点・面・マテリアル情報だけ引き抜いて
描画してみました。

暗!?しかも左右反転!!
Z座標と法線を反転させるのを忘れてたので、反転させてもう一度描画。


今度はうまく描画出来ました。

最後にマテリアルを適用させて描画しました。
MMDにはEmissive情報がないようなので、Diffuse色と同じ値を
入れることにしました。これをしないとモデルが暗くなるんですよね。



テクスチャを反映させて、明るく描画することが出来ました。
やはりMMDのモデルは綺麗ですね。

ひとまずこれでアニメーションのないモデルなら問題なく使えるようになりました。
武器とかマップ表示に使えそうですね。
次はボーンの読み込みまで行おうと思います。

2011年4月1日金曜日

XNAでメタセコイアモデルを直接読み込む Part3

今回は『法線のスムージング化』、『回転体の実体化』を行いました。

http://pr0jectze10.tuzigiri.com/

本当は曲面化にも対応させたいんだけど、計算方法がわからない・・・ 
というわけで、不具合が出ない限り次からMMDやります。