今回、描こうとしているダンボーは複数の立方体で構成されてます。
んで、動かしてなんぼのOpenGLじゃい!
というわけでウォーキングでもさせてみようと思ったらいきなり壁にぶち当たるわけですわ。
胴体をちょっと回転させただけで、腕や足の位置がコロコロ変わるじゃ~ん。
お、まいがっ。
手や足、頭は、胴体にひっついててなんぼだから、たえず胴体の回転、移動に影響を受けるんだよね~、そりゃそうだ。
どうすればいいかというと、変換行列を階層的に適用していくことで対応します。
まず、左腕からみたら、胴体の中心こそが自分の原点であって、実際の3次元空間の原点がどこかは知ったこっちゃないわけです。
なので、自分にとっての原点を胴体の中心として変換行列を作成します。
ここまでの変換行列が第一階層。
次に、胴体の移動や回転をおこなう変換行列を第二階層として準備。
でもって、胴体にはこの第二階層の行列のみ適用し、末端には第一階層の行列に、この第二階層の行列をかけあわせます。
この絵だと左から順に変換行列が適用されていって、最終的には左腕は胴体の傾きに連動した位置に表示される事になる。
右腕も、頭、足も同じです。
そんなわけで、左腕と右腕、胴体に適用する行列は以下のような階層構造を作り出す事になるわけっす。
で、枝分かれを増やして行く事で、もう少し複雑に、指→手→腕→胴体といったモデリングも可能になって、結城晶が裡門頂肘を打ったりするわけですな。
この枝わかれの部分で利用されるのが、遥か昔に取り上げた、現在の行列を保存したり、保存した行列を現在の行列に復帰させる
というわけです。
今回のサンプルでは、枝分かれの管理や行列の階層化をBoneというクラスで受け持たせています。
このBoneを胴体、頭、腕、足と用意してつないでいき、各Boneの動作を指定しているのがdanboというクラスです。
クラス説明
Light
ライトを設定するクラス。
Box
立方体を描画するクラス。
Bone
自分が従わせるべきBoneを管理し、自らも他のBoneに従うように指定できるようにして階層構造を構築する。
danbo
頭、ボディ、右腕、左腕、右足、左足をBoneで階層構造としてリンクする。
あと、一番のきもとなるのはBoneのdrawメソッドなんで、この動きをダンボーの頭と耳でちょこっと説明しときます。
注意:
1、OpenGLの行列は、描画に近い順から適用される。
2、danboでは-1~+1の立方体をスケーリング(BoneクラスのsetBoundsメソッド)する事で、頭や、腕、胴体を作っている。したがって、このスケーリング自体は変換行列の階層構造に含めるべきではない。実際のソースも
として、除外している。
じゃ、まずダンボーの頭の部分から
1、glRotatef(rotate_angle, rotate_x, rotate_y, rotate_z)で首のかしげを設定する。
2、glTranslatef(-origin_x * bounds_x, -origin_y * bounds_y, -origin_z * bounds_z)で回転中心を移動。頭の回転中心は首のあたりをイメージして、下の部分にしている。
3、[target draw]。このdraw命令で、ここまでに設定された変換行列が機能する。したがってダンボーの頭には、回転と移動のみが適用される。
4、次にfor (BoneLeaf* leaf in leaves) によって自分が管理するBoneを描画していく。頭の場合、左耳と右耳が管理対象のBone。右耳、左耳のBoneは独立しているのでglPushMatrix/glPopMatrixで囲い、ここまでに設定された行列が変化しないようにしている。
5、glTranslatef(leaf.x, leaf.y, leaf.z)で頭に対する耳の接続点に移動。
6、[leaf.target draw]、leaf.targetはBoneなので、1~3が実行される。
したがって耳に適用される変換行列は、頭の変換1、2に5の移動、自分の変換1、2ということになる。
今回は、耳が管理するBoneは無いので、ここで終わるけど、もしイヤリングなんかを耳の管理下のBoneとして設定すれば、もう一段階層が深く潜る事になるわけです。
結局、その繰り返しです。
ま~、こういうふうにアクションを用意しておいて、キャラクタを3次元空間内で縦横無尽に走らせるわけですな。
------------
サンプルプロジェクト:cardboard.zip
んで、動かしてなんぼのOpenGLじゃい!
というわけでウォーキングでもさせてみようと思ったらいきなり壁にぶち当たるわけですわ。
胴体をちょっと回転させただけで、腕や足の位置がコロコロ変わるじゃ~ん。
お、まいがっ。
手や足、頭は、胴体にひっついててなんぼだから、たえず胴体の回転、移動に影響を受けるんだよね~、そりゃそうだ。
どうすればいいかというと、変換行列を階層的に適用していくことで対応します。
まず、左腕からみたら、胴体の中心こそが自分の原点であって、実際の3次元空間の原点がどこかは知ったこっちゃないわけです。
なので、自分にとっての原点を胴体の中心として変換行列を作成します。
ここまでの変換行列が第一階層。
次に、胴体の移動や回転をおこなう変換行列を第二階層として準備。
でもって、胴体にはこの第二階層の行列のみ適用し、末端には第一階層の行列に、この第二階層の行列をかけあわせます。
この絵だと左から順に変換行列が適用されていって、最終的には左腕は胴体の傾きに連動した位置に表示される事になる。
右腕も、頭、足も同じです。
そんなわけで、左腕と右腕、胴体に適用する行列は以下のような階層構造を作り出す事になるわけっす。
で、枝分かれを増やして行く事で、もう少し複雑に、指→手→腕→胴体といったモデリングも可能になって、結城晶が裡門頂肘を打ったりするわけですな。
この枝わかれの部分で利用されるのが、遥か昔に取り上げた、現在の行列を保存したり、保存した行列を現在の行列に復帰させる
glPushMatrix/glPopMatrix
というわけです。
今回のサンプルでは、枝分かれの管理や行列の階層化をBoneというクラスで受け持たせています。
このBoneを胴体、頭、腕、足と用意してつないでいき、各Boneの動作を指定しているのがdanboというクラスです。
クラス説明
Light
ライトを設定するクラス。
Box
立方体を描画するクラス。
Bone
自分が従わせるべきBoneを管理し、自らも他のBoneに従うように指定できるようにして階層構造を構築する。
danbo
頭、ボディ、右腕、左腕、右足、左足をBoneで階層構造としてリンクする。
あと、一番のきもとなるのはBoneのdrawメソッドなんで、この動きをダンボーの頭と耳でちょこっと説明しときます。
注意:
1、OpenGLの行列は、描画に近い順から適用される。
2、danboでは-1~+1の立方体をスケーリング(BoneクラスのsetBoundsメソッド)する事で、頭や、腕、胴体を作っている。したがって、このスケーリング自体は変換行列の階層構造に含めるべきではない。実際のソースも
glPushMatrix();
glScalef(bounds_x, bounds_y, bounds_z);
[target draw];
glPopMatrix();
として、除外している。
じゃ、まずダンボーの頭の部分から
1、glRotatef(rotate_angle, rotate_x, rotate_y, rotate_z)で首のかしげを設定する。
2、glTranslatef(-origin_x * bounds_x, -origin_y * bounds_y, -origin_z * bounds_z)で回転中心を移動。頭の回転中心は首のあたりをイメージして、下の部分にしている。
3、[target draw]。このdraw命令で、ここまでに設定された変換行列が機能する。したがってダンボーの頭には、回転と移動のみが適用される。
4、次にfor (BoneLeaf* leaf in leaves) によって自分が管理するBoneを描画していく。頭の場合、左耳と右耳が管理対象のBone。右耳、左耳のBoneは独立しているのでglPushMatrix/glPopMatrixで囲い、ここまでに設定された行列が変化しないようにしている。
5、glTranslatef(leaf.x, leaf.y, leaf.z)で頭に対する耳の接続点に移動。
6、[leaf.target draw]、leaf.targetはBoneなので、1~3が実行される。
したがって耳に適用される変換行列は、頭の変換1、2に5の移動、自分の変換1、2ということになる。
今回は、耳が管理するBoneは無いので、ここで終わるけど、もしイヤリングなんかを耳の管理下のBoneとして設定すれば、もう一段階層が深く潜る事になるわけです。
結局、その繰り返しです。
ま~、こういうふうにアクションを用意しておいて、キャラクタを3次元空間内で縦横無尽に走らせるわけですな。
------------
サンプルプロジェクト:cardboard.zip