3Dグラフィックス入門(その2) GU描画
2005年 07月 19日
ああ、途中間があいてしまいましたね(^^;)
飽きたわけでわないんです。...違うんですG○O2が悪いんです。
...ええ、もう極悪ですとも。
前回は、物凄い勢いでGU初期化について説明しましたが、如何だったで
しょうか?正直むずいので、全部理解する必要ないです。こんなもんかな
程度に押さえといて下さいね。
さて、前回 『3Dグラフィックス入門(その1) GU初期化』でGUに対して
描画の準備が整いました。今回は、GUのプリミティブ(GUで用意して
くれている基本図形など)の描画について説明します。
尚、今回も盛りだくさんなのでデータ表記や、ジオメトリ変換用の関数
の説明は次回に回します(さぁ、のっけから雲行きが怪しく...^^)。
PSPSDKのcubeデモよりGU描画部分のみ抜粋しました。
==============================================
GU描画の例(PSPSDK cubeデモより抜粋)
==============================================
・・・ 中略(関数内のグローバル変数宣言部)
1: struct Vertex __attribute__((aligned(16)))
vertices[12*3] =
・・・ 中略(Vertex=頂点座標)のセット(ここにcubeの形状がっ)
2: };
・・・ 中略(メイン関数内描画ロジック)
3: ScePspFMatrix4 projection;
4. ScePspFMatrix4 view;
5: ScePspFMatrix4 world;
6: int val = 0;
// 描画用ループ
7: for(;;)
8: {
9: sceGuStart(0,list);
// 画面クリア
10: sceGuClearColor(0xff554433);
11: sceGuClearDepth(0);
12: sceGuClear(GE_CLEAR_COLOR|GE_CLEAR_DEPTH);
// cubeのマトリクスをセットする
13: matrix_identity((float*)&projection);
14: matrix_projection((float*)&projection,
5.0f,16.0/9.0f,0.01f,1000.0f);
15: sceGuSetMatrix(GU_MATRIX_PROJECTION,
&projection);
16: matrix_identity((float*)&view);
17: sceGuSetMatrix(GU_MATRIX_VIEW,&view);
18: matrix_identity((float*)&world);
19: matrix_translate((float*)&world,0,0,-3.0f);
20: matrix_rotate((float*)&world,
val * 0.79f * (M_PI/180.0f),
val * 0.98f * (M_PI/180.0f),
val * 1.32f * (M_PI/180.0f));
21: sceGuSetMatrix(2,&world);
・・・ 中略(テクスチャーやマテリアルのセット)
// cubeの描画
22: sceGuDrawArray(GU_PRIM_TRIANGLES,
GE_SETREG_VTYPE(GE_TT_32BITF,GE_CT_8888,
0,GE_MT_32BITF,0,0,0,0,0),12*3,0,vertices);
23: sceGuFinish();
24: sceGuSync(0,0);
25: sceDisplayWaitVblankStart();
26: sceGuSwapBuffers();
27: val++;
28: }
==============================================
【描画の大まかな流れについて】
例によってだらだら書いてますのでわかりにくいです(^^;)
なのでポイントをまず先に記載しますね。これに沿って説明
していきましょう。
・cube頂点座標を用意します。
・マトリクス演算用変数の宣言。
・描画用のループを用意します(無限ループ)
・描画用のコマンドリスト開始宣言をします(終了までが
描画対象です)
・ 画面をクリアします(描画前に必ずクリアします)
・透視変換用マトリクスをセットします。
・ビュー用マトリクスをセットします。
・ワールド座標用マトリクスをセットします。
(描画ループ内でcubeの形状、透視変換、ビューともに
毎回固定ですがワールド座標用のマトリクスに回転を
加える事でcubeを回転させています)
・GUプリミィティブ描画にてcubeの描画を行います。
・描画用のコマンドリスト終了宣言をし、GUに実行要求を行います。
・次の描画の準備をします。
・描画用のループに戻ります。
【cube頂点座標について】
ううっ、cube形状を定義するための重要な情報ですが詳細
次にまわします(その方が説明に都合が良いので)。ここは
Vertexという頂点定義の型があってそれをDMA転送のきり
の良い形で頂点の情報を定義するんだなぁと思ってください。
1行目から3行目がそうです。ここでセットされている頂点座標
がcubeの形(大きさや位置も含めた)になります。
1: struct Vertex __attribute__((aligned(16)))
vertices[12*3] =
・・・ 中略(Vertex=頂点座標)のセット(ここにcubeの形状がっ)
2: };
【マトリクス演算用変数について】
詳細は次回参照ですが、マトリクスとは行列の事を指します。
色んな行列を用意していますが、今回は透視変換用マトリクス、
ビュー用マトリクス、ワールド座標用マトリクスがあります。
凄い乱暴な言い方をしますが(次回説明するから^^;)
透視変換用マトリクスは3DからPSPの2D画面への置き換え
(レンダリングとかラスタライズとかいいます)で使います。ビュー
用マトリクスは3D空間の見える部分の切り出し、ワールド用
マトリクスはオブジェクトを配置している空間の操作を行うために
使用します(cubeしか配置してないのでワールドを回転させると
cubeが回転してみえるでしょ)。変数はScePspFMatrix4(4
列の行列からなるマトリクス)型で宣言します(3行から5行目まで)。
3: ScePspFMatrix4 projection;
4. ScePspFMatrix4 view;
5: ScePspFMatrix4 world;
【描画用のループを用意します(無限ループ)について】
GUに何か描画させるためにはひっきりなしに描画コマンドを送り
つづける必要があります(Windows見たいに他のアプリが画面を
更新する事はないので、別に画面変わんないなら止めてていいじゃん
と思うでしょうが、パッドとかのボタン押したかどうか判定も必要なので...)。
で、必ず描画を行うループが必要になります。ループ自体は無限ループ
で、これを抜けルためにはPSP本体のHOMEキーを押します(そのために
プログラム開始時点で割り込み用スレットの定義を行っていますが、3D
プログラミングとは何にも関係無いし定型的な処理なので割愛させて
いただきます)。HOMEキーを押すとループを抜けcubeプログラムは
終了し、PSPのブラウザへ戻ります。描画ループは7行目から28行目
までです。
意外に重要なのが6行目で宣言されているvalという整数の変数ですが
これはループを繰り返すごとに1づつカウントされていきます(27行参照)。
じゃ、valは何に使ってるのか?というとcubeの回転に使われてます。
27行をコメントアウトして無効化すると判りますが、cubeが回転しない
とてもつまらないプログラムになります。あと、valは無限にカウントしつづ
けますのでいつか(符号付32bit整数なので2の(32-1)乗-1まで
カウントしたところで何チャラオーバーフローでプログラム自体がストップ
すると思います。時間的にはさらに60で割ると秒数がでます。かなり
長い事頑張ります)。あくまでもサンプルなのでこういう作り方をしていますが
こういうのは見習わないようにね(^^;)
6: int val = 0;
7: for(;;)
8: {
・・・ 中略(描画の要求が入ります)
27: val++;
28: }
【GUコマンドリスト開始と終了について】
前回の『3Dグラフィックス入門(その1) GU初期化』で説明したのと
全く一緒なのでそちらを見てください。cubeの描画はsceGuSync()
実行時に行われます。
9: sceGuStart(0,list);
・・・ GUに送る要求(今回は計算済のマトリクスと
プリミティブ描画)
23: sceGuFinish();
24: sceGuSync(0,0);
【 画面クリアについて】
毎回描画を始める前に画面全体をクリアします(正確には描画バッファ
の内容を指定カラーで埋めるだけです)。10行目のsceGuClearColor()
で画面消去のための色を指定します。今回、描画バッファは
GE_PSM_8888(24Bitフルカラー)で指定しているので色は24bitです。
頭ffは固定です。
微妙に違うんですが、背景色としてsceGuClearColor()を考えてもいいかも
です。
あと被写界深度バッファ(これも前回説明しましたね)もクリアするので、
それも指定します。11行目のsceGuClearDepth()がそれにあたり
ます。
実際に描画バッファや被写界深度バッファを指定値でクリアするのは
12行目のsceGuClear()のお仕事です。ここら辺は10行目の指定色
値以外は固定でいいです。
// 画面クリア
10: sceGuClearColor(0xff554433);
11: sceGuClearDepth(0);
12: sceGuClear(GE_CLEAR_COLOR|GE_CLEAR_DEPTH);
【各マトリクスのセットについて】
詳細は次回に回しますがジオメトリのマトリクス演算に関しては
GEでは無くC言語のプログラムで行う事になります(理由は前回
説明しました)。matrix_XXXX()という関数がそれを行ってくれます。
大体パターンは下記の流れです。
・matrix_identity()でマトリクス変数の初期化
・マトリクスの変換
matrix_projection()で透視変換作成とか
matrix_translate()で移動とか
matrix_rotate()で回転とか
・マトリクス値のGUへのセット
sceGuSetMatrix()で行います(正確にはGE経由だと
思われ...)
変換が必要の無い物はこれで直に渡すですよ。
例によって使えないコメント付記しておきますね(^^)
// 透視変換用マトリクスをセットします。
13: matrix_identity((float*)&projection);
14: matrix_projection((float*)&projection,
5.0f,16.0/9.0f,0.01f,1000.0f);
15: sceGuSetMatrix(GU_MATRIX_PROJECTION,
&projection);
// ビュー用マトリクスをセットします。
16: matrix_identity((float*)&view);
17: sceGuSetMatrix(GU_MATRIX_VIEW,&view);
// ワールド座標用マトリクスをセットします。
// ほら、変数valを使用して回転させてるでしょ!
18: matrix_identity((float*)&world);
19: matrix_translate((float*)&world,0,0,-3.0f);
20: matrix_rotate((float*)&world,
val * 0.79f * (M_PI/180.0f),
val * 0.98f * (M_PI/180.0f),
val * 1.32f * (M_PI/180.0f));
21: sceGuSetMatrix(2,&world);
【GUプリミィティブ描画について】
既に頂点座標として定義してある変数を(cubeの頂点座標群である
vertices)使用してプリミティブの描画要求を行います。22行目の
sceGuDrawArray()がそれで、さらにGU_PRIM_TRIANGLES
を指定してますがこれは三角形(これこそが俗にいうポリゴンで
あります)です...やりました、ポリゴン描画まで行き着きました。
ううっ、思えば永い旅路でした(T_T)。
// cubeの描画
22: sceGuDrawArray(GU_PRIM_TRIANGLES,
GE_SETREG_VTYPE(GE_TT_32BITF,GE_CT_8888,
0,GE_MT_32BITF,0,0,0,0,0),12*3,0,vertices);
当然、色んなプリミティブをsceGuDrawArray()で描画できますが
違うプリミティブ(例えば線)とか描画するんなら、別の頂点座標変数
の用意が必要です(表記も微妙に違います)
sceGuDrawArray()のリファレンスはこんな感じで
PARAM1: prim - 描画するプリミティブのタイプ
現在PSPSDKで使用できるGUプリミティブ
(OpenGLとかのプリミティブもこんな感じです)
GE_PRIM_POINTS
GE_PRIM_LINES
GE_PRIM_LINESTRIPS
GE_PRIM_TRIANGLES
GE_PRIM_TRISTRIPS
GE_PRIM_TRIFANS
GE_PRIM_SPRITES
PARAM2: vtype - 頂点タイプ
GE_SETREG_VTYPE()マクロを指定して指定
します(pspgu.h参照)特に描画バッファのモード
変えないならこのままでも(^^;)
PARAM3: count - 頂点情報の数(頂点の数じゃないよ)
頂点情報vertices[12*3] と宣言
されてますので、ここで指定する数は
12*3です。
PARAM4: indices - 頂点情報を他のプリミティブと兼用する
わけではないので先頭の0でいいです。
兼用するなら配列内でのインデックスを
指定してください。
PARAM5: vertices - 頂点情報が格納されている変数のポインタ
今回はverticesを使用しますので、
verticesを指定します。
【次の描画について】
描画が行われましたので次の描画の準備をします。
・次のVBlank割り込を待ちます(25行)
・作業用バッファと描画用バッファを切り替えます(26行)。毎回描画用
バッファと作業用バッファを切り替えて使います。
25: sceDisplayWaitVblankStart();
26: sceGuSwapBuffers();
ぬおおおっ、今回も物凄い勢いで詰め込みましたがいかかだったでしょうか?
たかだか27行のプログラムにこんな説明が入るなんて...何て効率の悪い
...私ってば頭が悪いんでしょうか?(T_T)...既に日記じゃないし!
あ、次回で何とかジオメトリ変換とかマトリクスの救済をしたいです(^^;)
ここをクリックで人気blogランキングへ(ご協力お願いします)
飽きたわけでわないんです。...違うんですG○O2が悪いんです。
...ええ、もう極悪ですとも。
前回は、物凄い勢いでGU初期化について説明しましたが、如何だったで
しょうか?正直むずいので、全部理解する必要ないです。こんなもんかな
程度に押さえといて下さいね。
さて、前回 『3Dグラフィックス入門(その1) GU初期化』でGUに対して
描画の準備が整いました。今回は、GUのプリミティブ(GUで用意して
くれている基本図形など)の描画について説明します。
尚、今回も盛りだくさんなのでデータ表記や、ジオメトリ変換用の関数
の説明は次回に回します(さぁ、のっけから雲行きが怪しく...^^)。
PSPSDKのcubeデモよりGU描画部分のみ抜粋しました。
==============================================
GU描画の例(PSPSDK cubeデモより抜粋)
==============================================
・・・ 中略(関数内のグローバル変数宣言部)
1: struct Vertex __attribute__((aligned(16)))
vertices[12*3] =
・・・ 中略(Vertex=頂点座標)のセット(ここにcubeの形状がっ)
2: };
・・・ 中略(メイン関数内描画ロジック)
3: ScePspFMatrix4 projection;
4. ScePspFMatrix4 view;
5: ScePspFMatrix4 world;
6: int val = 0;
// 描画用ループ
7: for(;;)
8: {
9: sceGuStart(0,list);
// 画面クリア
10: sceGuClearColor(0xff554433);
11: sceGuClearDepth(0);
12: sceGuClear(GE_CLEAR_COLOR|GE_CLEAR_DEPTH);
// cubeのマトリクスをセットする
13: matrix_identity((float*)&projection);
14: matrix_projection((float*)&projection,
5.0f,16.0/9.0f,0.01f,1000.0f);
15: sceGuSetMatrix(GU_MATRIX_PROJECTION,
&projection);
16: matrix_identity((float*)&view);
17: sceGuSetMatrix(GU_MATRIX_VIEW,&view);
18: matrix_identity((float*)&world);
19: matrix_translate((float*)&world,0,0,-3.0f);
20: matrix_rotate((float*)&world,
val * 0.79f * (M_PI/180.0f),
val * 0.98f * (M_PI/180.0f),
val * 1.32f * (M_PI/180.0f));
21: sceGuSetMatrix(2,&world);
・・・ 中略(テクスチャーやマテリアルのセット)
// cubeの描画
22: sceGuDrawArray(GU_PRIM_TRIANGLES,
GE_SETREG_VTYPE(GE_TT_32BITF,GE_CT_8888,
0,GE_MT_32BITF,0,0,0,0,0),12*3,0,vertices);
23: sceGuFinish();
24: sceGuSync(0,0);
25: sceDisplayWaitVblankStart();
26: sceGuSwapBuffers();
27: val++;
28: }
==============================================
【描画の大まかな流れについて】
例によってだらだら書いてますのでわかりにくいです(^^;)
なのでポイントをまず先に記載しますね。これに沿って説明
していきましょう。
・cube頂点座標を用意します。
・マトリクス演算用変数の宣言。
・描画用のループを用意します(無限ループ)
・描画用のコマンドリスト開始宣言をします(終了までが
描画対象です)
・ 画面をクリアします(描画前に必ずクリアします)
・透視変換用マトリクスをセットします。
・ビュー用マトリクスをセットします。
・ワールド座標用マトリクスをセットします。
(描画ループ内でcubeの形状、透視変換、ビューともに
毎回固定ですがワールド座標用のマトリクスに回転を
加える事でcubeを回転させています)
・GUプリミィティブ描画にてcubeの描画を行います。
・描画用のコマンドリスト終了宣言をし、GUに実行要求を行います。
・次の描画の準備をします。
・描画用のループに戻ります。
【cube頂点座標について】
ううっ、cube形状を定義するための重要な情報ですが詳細
次にまわします(その方が説明に都合が良いので)。ここは
Vertexという頂点定義の型があってそれをDMA転送のきり
の良い形で頂点の情報を定義するんだなぁと思ってください。
1行目から3行目がそうです。ここでセットされている頂点座標
がcubeの形(大きさや位置も含めた)になります。
1: struct Vertex __attribute__((aligned(16)))
vertices[12*3] =
・・・ 中略(Vertex=頂点座標)のセット(ここにcubeの形状がっ)
2: };
【マトリクス演算用変数について】
詳細は次回参照ですが、マトリクスとは行列の事を指します。
色んな行列を用意していますが、今回は透視変換用マトリクス、
ビュー用マトリクス、ワールド座標用マトリクスがあります。
凄い乱暴な言い方をしますが(次回説明するから^^;)
透視変換用マトリクスは3DからPSPの2D画面への置き換え
(レンダリングとかラスタライズとかいいます)で使います。ビュー
用マトリクスは3D空間の見える部分の切り出し、ワールド用
マトリクスはオブジェクトを配置している空間の操作を行うために
使用します(cubeしか配置してないのでワールドを回転させると
cubeが回転してみえるでしょ)。変数はScePspFMatrix4(4
列の行列からなるマトリクス)型で宣言します(3行から5行目まで)。
3: ScePspFMatrix4 projection;
4. ScePspFMatrix4 view;
5: ScePspFMatrix4 world;
【描画用のループを用意します(無限ループ)について】
GUに何か描画させるためにはひっきりなしに描画コマンドを送り
つづける必要があります(Windows見たいに他のアプリが画面を
更新する事はないので、別に画面変わんないなら止めてていいじゃん
と思うでしょうが、パッドとかのボタン押したかどうか判定も必要なので...)。
で、必ず描画を行うループが必要になります。ループ自体は無限ループ
で、これを抜けルためにはPSP本体のHOMEキーを押します(そのために
プログラム開始時点で割り込み用スレットの定義を行っていますが、3D
プログラミングとは何にも関係無いし定型的な処理なので割愛させて
いただきます)。HOMEキーを押すとループを抜けcubeプログラムは
終了し、PSPのブラウザへ戻ります。描画ループは7行目から28行目
までです。
意外に重要なのが6行目で宣言されているvalという整数の変数ですが
これはループを繰り返すごとに1づつカウントされていきます(27行参照)。
じゃ、valは何に使ってるのか?というとcubeの回転に使われてます。
27行をコメントアウトして無効化すると判りますが、cubeが回転しない
とてもつまらないプログラムになります。あと、valは無限にカウントしつづ
けますのでいつか(符号付32bit整数なので2の(32-1)乗-1まで
カウントしたところで何チャラオーバーフローでプログラム自体がストップ
すると思います。時間的にはさらに60で割ると秒数がでます。かなり
長い事頑張ります)。あくまでもサンプルなのでこういう作り方をしていますが
こういうのは見習わないようにね(^^;)
6: int val = 0;
7: for(;;)
8: {
・・・ 中略(描画の要求が入ります)
27: val++;
28: }
【GUコマンドリスト開始と終了について】
前回の『3Dグラフィックス入門(その1) GU初期化』で説明したのと
全く一緒なのでそちらを見てください。cubeの描画はsceGuSync()
実行時に行われます。
9: sceGuStart(0,list);
・・・ GUに送る要求(今回は計算済のマトリクスと
プリミティブ描画)
23: sceGuFinish();
24: sceGuSync(0,0);
【 画面クリアについて】
毎回描画を始める前に画面全体をクリアします(正確には描画バッファ
の内容を指定カラーで埋めるだけです)。10行目のsceGuClearColor()
で画面消去のための色を指定します。今回、描画バッファは
GE_PSM_8888(24Bitフルカラー)で指定しているので色は24bitです。
頭ffは固定です。
微妙に違うんですが、背景色としてsceGuClearColor()を考えてもいいかも
です。
あと被写界深度バッファ(これも前回説明しましたね)もクリアするので、
それも指定します。11行目のsceGuClearDepth()がそれにあたり
ます。
実際に描画バッファや被写界深度バッファを指定値でクリアするのは
12行目のsceGuClear()のお仕事です。ここら辺は10行目の指定色
値以外は固定でいいです。
// 画面クリア
10: sceGuClearColor(0xff554433);
11: sceGuClearDepth(0);
12: sceGuClear(GE_CLEAR_COLOR|GE_CLEAR_DEPTH);
【各マトリクスのセットについて】
詳細は次回に回しますがジオメトリのマトリクス演算に関しては
GEでは無くC言語のプログラムで行う事になります(理由は前回
説明しました)。matrix_XXXX()という関数がそれを行ってくれます。
大体パターンは下記の流れです。
・matrix_identity()でマトリクス変数の初期化
・マトリクスの変換
matrix_projection()で透視変換作成とか
matrix_translate()で移動とか
matrix_rotate()で回転とか
・マトリクス値のGUへのセット
sceGuSetMatrix()で行います(正確にはGE経由だと
思われ...)
変換が必要の無い物はこれで直に渡すですよ。
例によって使えないコメント付記しておきますね(^^)
// 透視変換用マトリクスをセットします。
13: matrix_identity((float*)&projection);
14: matrix_projection((float*)&projection,
5.0f,16.0/9.0f,0.01f,1000.0f);
15: sceGuSetMatrix(GU_MATRIX_PROJECTION,
&projection);
// ビュー用マトリクスをセットします。
16: matrix_identity((float*)&view);
17: sceGuSetMatrix(GU_MATRIX_VIEW,&view);
// ワールド座標用マトリクスをセットします。
// ほら、変数valを使用して回転させてるでしょ!
18: matrix_identity((float*)&world);
19: matrix_translate((float*)&world,0,0,-3.0f);
20: matrix_rotate((float*)&world,
val * 0.79f * (M_PI/180.0f),
val * 0.98f * (M_PI/180.0f),
val * 1.32f * (M_PI/180.0f));
21: sceGuSetMatrix(2,&world);
【GUプリミィティブ描画について】
既に頂点座標として定義してある変数を(cubeの頂点座標群である
vertices)使用してプリミティブの描画要求を行います。22行目の
sceGuDrawArray()がそれで、さらにGU_PRIM_TRIANGLES
を指定してますがこれは三角形(これこそが俗にいうポリゴンで
あります)です...やりました、ポリゴン描画まで行き着きました。
ううっ、思えば永い旅路でした(T_T)。
// cubeの描画
22: sceGuDrawArray(GU_PRIM_TRIANGLES,
GE_SETREG_VTYPE(GE_TT_32BITF,GE_CT_8888,
0,GE_MT_32BITF,0,0,0,0,0),12*3,0,vertices);
当然、色んなプリミティブをsceGuDrawArray()で描画できますが
違うプリミティブ(例えば線)とか描画するんなら、別の頂点座標変数
の用意が必要です(表記も微妙に違います)
sceGuDrawArray()のリファレンスはこんな感じで
PARAM1: prim - 描画するプリミティブのタイプ
現在PSPSDKで使用できるGUプリミティブ
(OpenGLとかのプリミティブもこんな感じです)
GE_PRIM_POINTS
GE_PRIM_LINES
GE_PRIM_LINESTRIPS
GE_PRIM_TRIANGLES
GE_PRIM_TRISTRIPS
GE_PRIM_TRIFANS
GE_PRIM_SPRITES
PARAM2: vtype - 頂点タイプ
GE_SETREG_VTYPE()マクロを指定して指定
します(pspgu.h参照)特に描画バッファのモード
変えないならこのままでも(^^;)
PARAM3: count - 頂点情報の数(頂点の数じゃないよ)
頂点情報vertices[12*3] と宣言
されてますので、ここで指定する数は
12*3です。
PARAM4: indices - 頂点情報を他のプリミティブと兼用する
わけではないので先頭の0でいいです。
兼用するなら配列内でのインデックスを
指定してください。
PARAM5: vertices - 頂点情報が格納されている変数のポインタ
今回はverticesを使用しますので、
verticesを指定します。
【次の描画について】
描画が行われましたので次の描画の準備をします。
・次のVBlank割り込を待ちます(25行)
・作業用バッファと描画用バッファを切り替えます(26行)。毎回描画用
バッファと作業用バッファを切り替えて使います。
25: sceDisplayWaitVblankStart();
26: sceGuSwapBuffers();
ぬおおおっ、今回も物凄い勢いで詰め込みましたがいかかだったでしょうか?
たかだか27行のプログラムにこんな説明が入るなんて...何て効率の悪い
...私ってば頭が悪いんでしょうか?(T_T)...既に日記じゃないし!
あ、次回で何とかジオメトリ変換とかマトリクスの救済をしたいです(^^;)
ここをクリックで人気blogランキングへ(ご協力お願いします)
by pspborder
| 2005-07-19 11:18
| PSP開発