スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

"アニメーション付きXファイル"について

【リンク】 ◆解説系TOP◆

アニメーションを理解するために、Xファイルを解析してみたりしたのですが
色々と分かってきました。

別にアニメーションの無いXファイルにも共通して言えることもありますが、
とりあえず気付いたことを纏めてみます
また、一番最後に Xファイルのサンプルを用意しています。

↓以下、気付いた事


【1】最初の"template"は、削除しても動く

Xファイルの初めには、下記の様なtemplateがたくさんあるのですが、
----------------------------------------
template Header {
<3D82AB43-62DA-11cf-AB39-0020AF71E433>
WORD major;
WORD minor;
DWORD flags;
}

--------------------------------------
削除しても動くようです。

一番初めの可変長ヘッダー("xof 0302txt 0064"とか)があれば動く模様。
ついでに、可変長ヘッダーの説明。
------------------------------------------------------------------
xof 0302txt 0032

「Xファイルだ」と識別する為の文字列。
メジャーバージョンを示す。
マイナーバージョンを示す。
テキストファイルである事を示す。バイナリの場合はtxt→binとなる。
数字を32bitで読み込む。

------------------------------------------------------------------

あとHeaderについて。
---------------------
Header {
1; //メジャーバージョン
0; //マイナーバージョン
1; //フラグ
}

---------------------
メジャーバージョンとかマイナーバージョンって何?と思うかもしれませんが、
自分でも良く分かりません。 "おまじない"と考えて良さそうです。


【2】アニメーションは、後付けで実装する。

まず、単純な箱型のモデルを表示するXファイルは下記の様になります。
---------------------------------------------------------
//前略

Frame Box
{
FrameTransformMatrix{ //座標変換行列 }
Mesh {
//頂点、法線などの情報
}
}

---------------------------------------------------------

この3Dモデル"Box"をアニメーションさせたい場合は下記の様に
追記する形で実装できます。
---------------------------------------------------------
//前略

AnimTicksPerSecond {
60;
}

Frame Box
{
FrameTransformMatrix{ //座標変換行列 }
Mesh {
//頂点、法線などの情報
}
}

AnimationSet AnimationSet1
{
Animation Animation1
{
{Box}
AnimationKey
{
2; // Position
3;
0;3; 0.000000,0.000000,0.000000;;, //x,y,z
120;3; 1.000000,0.000000,0.000000;;,
240;3; 0.000000,0.000000,0.000000;;,
}
}
}

---------------------------------------------------------
で、簡単に説明しておきますと、まず1秒間に何コマあるのか?
AnimTicksPerSecondで指定します。
今回は、1秒間に60コマです。

次に、AnimationKeyのすぐ後にある数字は、
「どんな変換をするのか?」のフラグになります。

【フラグ一覧】------------------
0 ・・・ 回転
1 ・・・ 拡大、縮小 (スケール変換)
2 ・・・ ポジションのオフセット
3 ・・・ 行列を使って変換。

-----------------------------
上記では「2」になっているので、ポジションのオフセットです。
(上記では、 X座標が1の所へ移動し、その後 X座標が0の所に戻るようになってます)

そして、次にある数字がアニメーションのキーの数です。
今回は、 0コマ目、120コマ目、240コマ目の3つのキーがあるので「3」となっています。


【3】複数の変換をしたい場合は、AnimationKeyを追加する。

先ほどは、ポジションのオフセットしかしていませんでしたが、
他の拡大、縮小などの変換も同時に行いたい場合は、
AnimationKeyを増やす事で実装できます。
------------------------------------------------
//前略
AnimationSet AnimationSet1
{
Animation Animation1
{
{Box}
AnimationKey
{
2; // Position
3;
0;3; 0.000000,0.000000,0.000000;;,
120;3; 1.000000,0.000000,0.000000;;,
240;3; 0.000000,0.000000,0.000000;;,
}
AnimationKey
{
1; // Scale
3;
0;3; 1.000000,1.000000,1.000000;;,
120;3; 1.500000,1.500000,1.500000;;,
240;3; 1.000000,1.000000,1.000000;;,
}

}
}
------------------------------------------------
上記のように追記させた場合、
x座標が1の所に移動しながら、xyzそれぞれの長さが1.5倍に変化していきます。


【4】複数のモデルを動かしたい場合は、Animationを増やす。

先ほどまではBoxというFrameを色々と動かすだけでしたが、Frameが複数ある場合は
Animationの部分を増やして値を設定します。

例えば、 Frameが "RedBox""BlueBox"の2つ ある場合は、
下記の様になります。
--------------------------------------------------------------
AnimationSet AnimationSet1
{
Animation Animation1
{
{RedBox}
AnimationKey
{
2; // Position
3;
0;3; 0.000000,0.000000,0.000000;;,
150;3; 0.500000,0.000000,0.000000;;,
300;3; 0.000000,0.000000,0.000000;;,
}
}
Animation Animation2
{

{BlueBox}
AnimationKey
{
2; // Position
3;
0;3; 4.000000,0.000000,0.000000;;,
150;3; 4.000000,0.500000,0.000000;;,
300;3; 4.000000,0.000000,0.000000;;,
}
}
}
--------------------------------------------------------------
赤い箱は左右に、青い箱は上下に動くようになります。
別々の動きをさせる事が出来ました。


【5】マルチアニメーションはAnimationSetを増やす。

例えば、人型のモデルの場合、飛ぶ、座る、蹴るという様な、
複数のアニメーションを持たせると思います。
この場合は、ひとつのXファイル上にAnimationSetを複数用意する事で実装できます。
-------------------------------------------------------
//前略

AnimationSet AnimationSet1 //飛ぶ
{
//詳細は省略
}

AnimationSet AnimationSet2 //座る
{
//詳細は省略
}

AnimationSet AnimationSet3 //蹴る
{
//詳細は省略
}

-------------------------------------------------------
で、Xファイルビューアで見る場合、どうやら
一番最後のアニメーションセットが実行されるっぽいです。

なので、上記の場合は蹴るアニメーションが実行されます。

当然、 AnimationSet2(座る)AnimationSet3(蹴る)よりも
下側にコピーペーストすれば、今度は座るアニメーションが実行されます。

プログラムで実行する場合は、コレを適宜 切り替えるんでしょうね。




最後に、Xファイルのサンプルを置いておきます。 参考にどうぞ(`・ω・´)
(テキストファイルなので、拡張子を.xに変更してください)

Sample1
※templateを削除したサンプルです。
Sample2
※Frame"Box"をアニメーションをさせるサンプルです。
Sample3
※"Box"に対して、オフセットと拡大縮小を同時に行うサンプルです。
Sample4
※"BoxRed"と"BoxBlue"に、それぞれ別の動きをさせるサンプルです。
Sample5
※サンプル4の"BoxRed"を親、"BoxBlue"を子、の親子関係に変更したサンプルです。
Sample6
※複数のアニメーションセットを持たせたサンプルです。
スポンサーサイト

テーマ : ゲーム製作 関連 - ジャンル : ゲーム

コメント

No title

アニメーション管理クラスの作成に突入しました^^
しかしなぜか、ヒープが壊れるというエラーが連発。。。
Xファイルのアニメーションの読み込み部分でmapとlistを使ってるんですが、なかなかうまく行きません。
読み込みは出来ているんですが。。。

conioさんは、どのように実装してますか?
Xファイルの形式ですと、どうも最初にアニメーションの数を数えるという方法よりも、STLのlistなどを使って、どんどん伸ばしていく形式のほうがよいと思ったのですが、なぜかヒープが壊れるというエラーが出てきてしまうんですよね・・・。

No title

おぉ順調に進んでますねv-411

バグに関してですが、、
自分もヒープが破壊されているバグに遭遇した事があります。

自分の場合、原因は作成したバッファの範囲を超える領域に書き込みを行っていたからでした。
-------------------------------------------------
MY_VERTEX* v;
g_pD3DDevice->CreateVertexBuffer(
3*sizeof(MY_VERTEX),
D3DUSAGE_WRITEONLY,
MY_VERTEX_FVF,
D3DPOOL_MANAGED,
&g_vb,
NULL);
g_vb->Lock( 0, 0, (void**)&v, 0);

v[0].x =  //中略
v[1].x =  //中略
v[2].x =  //中略
v[3].x =  //中略
-------------------------------------------------
※頂点を3つ分しか確保してないのに、
4つ目の頂点まで書き込み
をしている。

どこか範囲外の訳の分からない領域に書き込みを行ってないかを
もう一度 変数の値とか調べてみるといいかもしれません。

アニメーションセットに関しては、std::vectorで随時増やす感じにしました。

下記のような感じです。↓

switch(キーワード){
case "AnimationSet"だったら
LoadAnimationSet();//アニメーションセットを読み込んで追加する関数
break;
default:
break;
}

No title

どうやら、少し謎が解けた気がしています。
STLに渡しているクラスにポインタが含まれていて、かつそこでnewすると、こういうエラーが生じているみたいです。
うーん、なぜだろう。。。
STLのlistとかで、push_backは内部ではどんなことをしてるんでしょうね^^;
operator=を自作しても、ダメでしたし・・・。

実装は、やはり自分と似た感じですね^^
正直自分もvectorかlistか迷った感じです。
この場合でしたら、追加・削除を行うことってないのでvectorのほうがいいのかなっと思ったりしてます。

あと、何度も聞いてしまって申し訳ないのですが、アニメーションの読み込みが完了したとして、実際に変換する時って、やはりこれも自作しましたか、それとも、DirectXの補助関数の使用でしたか?
一応、私が考えているのは「ゲームプログラマになる前に覚えておきたい技術」に載ってた、線形補間を自分で作成したほうがいいのかなと思ってるのですが。。。
OpenGLだとこのような関数がない(もしかしたらあるかも?)ので^^;

No title

それでしたら、 そのnewが原因ですね!

例えば下記のような場合、
-------------------------------------
class Test {
public:
  Test(){ a = new int; };
  ~Test(){delete a;};
private:
  int* a;
};

int main(void){
  {
    Test test;        【1】
    std::vector<Test> tVec;
    tVec.push_buck(test); 【2】
  }【3】
return(0);【4】
}
-------------------------------------

【1】Testのコンストラクタが呼ばれて、int型をnew。(変数test)

【2】vectorがTest型の配列(要素数1)を確保。
【2】確保したときに、コンストラクタが呼ばれてint型をnew。(tVec[0])
【2】その後、 変数testがtVec[0]に上書きされる。
(元々あったtVec[0]のnewしたintは、
  deleteされないままポインタが書き換えられる。


【3】変数testが消滅。デストラクタが呼ばれて、int型のポインタがdelete。
tVec[0]のa == testのaなので、
tVec[0]は、deleteされた不定のポインタを持っている状態になっている。

【4】vectorが確保した、要素数1のTest型配列が開放される。
デストラクタが呼ばれ、
tVec[0]は、 既に駄目になっているポインタaをdelete。 (エラー発生。)


こんな感じでバグります。
書き換えられてしまった、元々のtVec[0]のaはdeleteされないので
メモリリークが発生しますし、
vectorが消滅する時には不定のポインタをdeleteする事になってプログラムが壊れます。


----------------------------------------------
アニメーションセットは、同じ理由でvectorを選びましたv-290
(アニメーションセットは、動的に挿入したり、削除したりしないと思った為)

あとアニメーションの実行に関しては、
回転行列の補完のみ補助関数を使ってます。
(D3DXQuaternionSlerp 関数 と、
D3DXQuaternionRotationMatrix関数。)

-----------------------------------------------------
AnimationKey {
0;
2;
10;4;1.000000,-0.000000,-0.000000,-0.000000;;,
20;4;0.693484,-0.000000,-0.720472,-0.000000;;,
}
-----------------------------------------------------
例えば、上記のXFileで、
アニメーションの時間が15だった場合、

DWORD DifTime = 20 - 10;
DWORD NowTime = 15 - 10;

float rate = NowTIme / DifTime; (0.5。 Key1とKey0の、丁度半分。)

D3DXQuaternionSlerp( &qOut, &qKey0, &qKey1, rate); (クォータニオンで補間)
D3DXMatrixRotationQuaternion( &Mat, &qOut );(クォータニオンから、行列生成。)

後は算出された行列で描画。

こんな感じにしてます。
もし、補助関数を使わないのであれば、
具体的なクォータニオンから行列の変換や、
クォータニオンの補間に関しては、

「実例で学ぶ ゲーム3D数学」という本に詳しく載ってますv-411

No title

えと、conioさんが仰っているのは、クラスが持っているポインタの挙動のコピーに関する問題ですよね?
うーん、一応コピーコンストラクタとかも自作しておいてポインタの挙動についてもきちんと実装したんですがねぇ。。。
それでも問題が生じてしまうのはなぜなんでしょうか??
でも、もう諦めて、STL使うことにしました^^

クォータニオン??
名前だけは聞いたことがあります。。。
xファイルに入ってる回転を表すものってクォータニオンなんですか!?
うーん、でも考えてみればそうなのかも。
クォータニオン補間、別名球面線形補間って言うらしいですね!
うむむ、これは勉強せねば。

No title

ん~おかしいですねv-292
内部でnewをするクラスであっても、
コピーコンストラクタを定義していればSTLを利用してもバグらない筈なのですが・・・。
--------------------------------------------------
class Test{
public:
  Test(){
    m_p = new int;
    *m_p = 5;
  }
  Test(const Test& t){
    m_p = new int;
    *m_p = *t.m_p;
  }
  ~Test(){
    delete m_p;
    m_p = NULL;
  }
private:
  int* m_p;
};

int main (void) {
  std::vector<Test> tVec;
  {
    Test t;
    tVec.push_back(t);
  }
  return 0;
}

--------------------------------------------------
他の原因として考えられるのは
deleteとすべき所をdelete[]としてる、とかぐらいですかね?

ともかく、STLは比較的 バグらないように色々と試行錯誤されてるので、
自分でnewをしたりする機構をつくるよりかは、
STLを使った方が安全かと思われますv-290

>クォータニオン
そうです。
上記でもちょっと書きましたが、
----------------------------------------------------------
10;4;1.000000,-0.000000,-0.000000,-0.000000;;,
----------------------------------------------------------
↑このように、 4つの浮動小数点が並んでいるもの
クォータニオンの情報になります。

また、クォータニオンは"四元数"とも呼ばれていて、4つの値で表されます。

で、Xファイルでは、左から順に
W X Y Z という風にならんでおり、

回転が無ければ、値は
1.000000,-0.000000,-0.000000,-0.000000
になります。
回転がある場合は、Wと、回転がある座標軸の値が変わります。

(以前のコメントで記述したXファイルの一部では「Y軸中心の回転」をさせてるので、Yの部分が-0.720472に変化してます)

球面線形補間とかは、 本当に難しそうな原理ですが、
理解しておく必要はありそうです。

※理解しておいた方が、「なぜ表示される場所がおかしいのか?」みたいなバグも、原因が突き止めやすくなりますし、

また、 中身を理解せずに 補助関数を使ってゲームを作っても、
 「基礎の部分の理解が無い」という事で、 (ゲーム企業に提出する際は)あまり評価されなかったりするみたいですし。

No title

休み1日使って、アニメーション部分とフレーム部分の読み込み完成しました!!
結局のところ、STLのvectorを使って最終的に実装しました^^;
そしたら、すんなりと。。。
ああ、早めにつかっておけばよかったorz
いやはや、今日はさえていたようです^^;
再帰関数の実装にもまるぺけさんのところやセガの人の本を前もって読んでいたためか、あまり躓かなくすんなりいけました^^
何度も挫折したけど、これでやっといろいろとわかってきました。

とはいっても、またまた問題が発生。。。
xファイルに書いてあるFrameTransformMatrixって全体の変換を行った後の行列が書いてあるため、OpenGLで扱うためにはこの行列をそれぞれの変換に分解する必要があるみたいです。
これってどうしたほうがいいんでしょうね・・・。
DirectXなら、SetTransform(?)で行列を一回渡してあげれば簡単なんですがね。。。

No title

OpenGLって何か色々と仕様が違うっぽいですね。
とりあえず、回転・拡大縮小・平行移動の成分が混ざっているときは、

拡大縮小 → 回転 → 平行移動 の順に処理すれば問題ないと思います。

<ちょっと調査中です>

No title

無事動作付きで表示できました!
なんかすごくうれしいです^^

解決手段ですが、OpenGLにも似たような関数として、glMultMatrixfというのがあって、これにfloat型の16要素をもつ配列を渡してあげることで変換できるそうです。

これでやっとのことで、アニメーション付きのxファイルの表示が出来るようになりました。

次はいよいよスキンメッシュです^^

ただやはり、変換行列の分解方法は依然としてわかってないです^^;

No title

おお動きましたか!
順調に進んでいるようでなによりですv-290

あと、FrameTransformMatrixに関してですが、
これは特に分解する必要は無いんじゃないかなと思います。

Xファイルには、 拡大縮小・回転・平行移動
動きが別々に定義されてますので、(AnimationKeyの部分)
適宜、これらの情報から合成された行列を渡してあげれば描画されるんじゃないかな、と。

http://wisdom.sakura.ne.jp/system/opengl/gl11.html
このサイトのやり方でいうと、

平行移動の成分を、a12 ・ a13 ・ a14、
拡大縮小の成分を、
a1 ・ a2 ・ a3 (X軸回転)
a5 ・ a6 ・ a7 (Y軸回転)
a9 ・ a10 ・ a11 (Z軸回転)

回転の成分を、
a1 ・ a5 ・ a9、
a2 ・ a6 ・ a10、
a3 ・ a7 ・ a11、
(行列の左上、 3×3の部分)

に、それぞれ値を入れた後、glMultMatrixf関数に変換した行列を入れてあげれば良いのではないでしょうか。

X軸方向に2倍、Y軸方向に5倍、Z軸方向に3倍、
X軸中心に90度回転、 
その後 X軸方向に1、Y軸方向に2、Z軸方向に4平行移動

という風な、アニメーションが定義されている場合は、

最初の行列が単位行列として、
----------------------------------
a6 = 0;
a7 = 1;
a10 = -1;
a11 = 0;

a1 *= 2; a2 *= 2; a3 *= 2;
a5 *= 5; a6 *= 5; a7 *= 5;
a9 *= 3; a10 *= 3; a11 *= 3;

a13 = 1; a14 = 2; a15 = 4;
----------------------------------
このように計算して

2 0 0 1
0 0 -3 2
0 5 0 4
0 0 0 0

という行列を作り、後はこの行列を渡せば大丈夫と思います。
コメントの投稿
管理者にだけ表示を許可する



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。