2009年6月29日月曜日

一応ちゃんと表示


Blenderで出力した頂点を意味のある順番で表示するためには、Faceを使う必要がありそうです。
Faceには面を構成する頂点の位置が順番に保持されているので、この順で頂点を結んでやれば面として表示することができそうです。
ただ、これだと面単位で描画することになるので、GL_QUAD_STRIPTで一気に出力することができなくなりますが、後で考えることにして今のところはこれで進めます。

データのフォーマットを次のようにしました。
F 頂点数 頂点番号1 頂点番号2 頂点番号3..

1桁目のFは例によって識別子で、その後に頂点数とその数だけ頂点番号が並びます。今のところポリゴンは四角形にしてるので頂点数はなくてもいいんですが、とりあえずつけときます。
面の数だけこの行が出力されます。

面の数はオブジェクトの行に追加しときました。
O オブジェクト名 頂点数 面数

Pythonのソースは次のようになります。

:
for f in mesh.faces:
out.write("F %d" % len(f.verts))
for v in f.verts:
out.write(" %d" % v.index)
out.write("\n")

で、出力されたデータを読み取るC++のソースが次のようになります。

:
case 'F':
Face* f = faces[faceCount]; // Faceは面情報を保持する構造体
in >> f->numVerts;
if (in.fail()) break;
++faceCount;
f->verts = new int[f->numVerts];
for (unsigned int i=0; i<numVerts; i++) {
unsigned int v;
in >> v;
if (in.fail()) {
break;
} else {
f->verts[i] = v;
}
}
:

んー、ちょっとswich文の中が長くなってきたので、整理した方がよさそうです。

で、描画する処理は次のようになります。


glBegin(GL_QUADS);
for (unsigned int i=0; i<faceCount; i++) {
Face* f = faces[i];
for (unsigned int j=0; j<f->numVerts; j++) {
glVertex3f(v[faces[i]->verts[j]);
}
}
glEnd();

GL_QUAD_STRIPTからGL_QUADSになりました。頂点の数によってGL_TRIANGLESにすると三角形のポリゴンにも対応できます。スピードがあれですが...

ま、なんにしてもひとまず形のわかるものが表示できました。モデルの形状は前回から少し弄ってます。

2009年6月27日土曜日

ブログの引っ越し

ブログをはてなからこっちへ引っ越しました。
今までの記事をほとんどコピペで移したので大変でした。といっても数は大した量ではないし、コメントも皆無(^^;)だったので、ある意味やり易かったんですが...
でも、はてなの記法に慣れていたので、htmlで書き直すのに手間取ったり、更新とやり直しを繰り返しているうちにGoogleさんから、スパム ブログの嫌疑をかけられたり...とりあえず落ち着きました。

Bloggerも機能追加していろいろ遊べそうなので徐々に覚えていくつもりです。

2009年6月23日火曜日

とりあえずなんか表示?


昨日つくったデータを読み込む部分の実装です。と、その前に、頂点の数も予め分かった方がいいので、データに頂点数も出力するようにしました。次のようなフォーマットになります。
O オブジェクト名 頂点数
V x座標1 y座標1 z座標1
V x座標2 y座標2 z座標2
:

1行目のO(オーです。ゼロではありません。)で始まる行にオブジェクト(モデル)名と頂点数を出力するようにしました。オブジェクト名も出力するようにしたのはただなんとなくです。OではなくモデルのMでもよかったんですが、ゆくゆくマテリアルでMは使うかなと思い、Oにしときました。ま、気がかわったらまた直せばいいし。

pythonスクリプトに以下の処理を追加です。

out.write("O %d¥n" % (obj.name, len(mesh.verts))

で、次はC++のプログラムです。データを読み込む部分は次のような感じです。

std::string name; // オブジェクト名
Vertex *v; // 頂点の配列(Vertexはfloat型のx,y,zをメンバに持つ構造体)

int num = 0; // 頂点の数
unsigned int idx = 0;
// データファイルをオープン
std::ifstream fin("model.dat");
while (fin.eof()) {
char id; // 識別子
std::string line; // 1行分のデータ
getline(fin, line);
// ストリングストリームにした方が都合がいいので...
std::istringstream sin(line);
sin >> id;
if (sin.fail()) continue;
switch (id) {
case 'O':
sin >> name >> num;
if (!sin.fail()) v = new Vertex[num];
break;
case 'V':
if (idx < num) {
sin >> v[idx].x >> v[idx].y >> v[idx].z;
++idx;
}
break;
}
}
fin.close();

で、読み込んだデータをGL_QUAD_STRIPで一気に表示させてやります。

glBegin(GL_QUAD_STRIP);
for (unsigned int i=0; i<num; i++) {
glVertex3f(v[i].x, v[i].y, v[i].z);
}
glEnd();

結果は...ぐちゃぐちゃでした。どうも出力した頂点の並びがGL_QUAD_STRIPで想定する並びでは出力されてないようです。

うーむ...

2009年6月22日月曜日

Exporter

Blenderで作ったモデルデータのエクスポートですが、最初から全部やるのはアレなので、とりあえず頂点データだけエクスポートしてみます。

その前に、データのフォーマットを決めなければなりません。扱いを簡単にするために次のような簡単なテキスト形式にします。
V x座標1 y座標1 z座標1
V x座標2 y座標2 z座標2
:

1桁目のVは頂点データを表す識別子です。今は頂点データだけですが、のちのち追加していくつもりです。

頂点データをエクスポートする関数は次のようになります。

def write(filename):
sce = Scene.getCurrent(); # 現在のシーン
obj = sce.objects.active # 選択中のオブジェクト
if not obj or obj.type != 'Mesh':
# 選択中のオブジェクトがメッシュ型でなければエラー表示して中止
Draw.PupMenu('not an mesh object')
return

mesh = obj.getData(mesh=True) # メッシュデータを取得
out = file(filename, "w") # ファイルを開く
for v in mesh.verts: # 頂点の数だけ繰り返し
out.write("V %f %f %f¥n" % (v.co.x, v.co.y, v.co.z))

出力ファイル名を選択する画面を開くために次の呼び出しを追加します。

Blender.Window.FileSelector(write, "Export")

最初の引数のwriteがファイル選択後に呼び出す関数(上でつくったやつ)で、2番目の引数は選択ボタンのラベルです。
あと、スクリプトの最初の方に、スクリプトのメニューグループや名前、インポートするモジュール名なんかを書いておく必要があります。

#!BPY
# -*- coding: utf-8 -*-
"""
Name: 'MyModel'
Blender: 248
Group: 'Export'
Tooltip: 'MyModel exporter'
"""
import bpy
import Blender
from Blender import *
:

このスクリプトをスクリプト用のフォルダに保存して、Scripts Windowで「Scripts」→「Update Menus」とやると、スクリプトがメニューに反映(上の場合、Exportメニューに)されて実行できるようになります。(詳しくはBlenderのチュートリアルやらのページに書いてあります)

で、昨日のデータをエクスポートした結果が次です。
V -0.230893 1.000000 -1.000000
V -0.230893 -1.000000 -1.000000
V 12.300001 -0.572064 -1.000000
V 12.299997 0.572064 -1.000000
V -0.230895 0.999999 1.000000
V -0.230890 -1.000001 1.000000
V 12.300003 -0.572064 1.000000
V 12.299999 0.572064 1.000000
V 5.017009 0.752617 -1.000000
V 5.636728 0.752617 -1.000000
V 5.017009 -0.752617 -1.000000
V 5.636729 -0.752617 -1.000000
V 5.017009 0.752617 1.000000
V 5.636729 0.752617 1.000000
V 5.017009 -0.752618 1.000000
V 5.636729 -0.752617 1.000000

このデータをプログラムで読み込んで表示してやればいいわけです。座標データをコード中に書かなくて済むので、これができるだけでも助かりますね...先は長いですが。

2009年6月21日日曜日

Blender


ボーンアニメーションの理論が分かっても、実装するとなると肝心のアニメーションさせるモデルのデータを用意する必要があります。

コード中にちまちまと座標等のモデルデータを書いてできなくもありませんが、モデルが複雑になるとかなり面倒です。

そこで必要になるのがモデリングソフトということになります。商用、フリーのものいろいろなソフトがあるのですが自分はBlenderを使うことにします。Blenderは有名なオーブンソースの3Dモデリングソフトです。かなり高機能なんですがUIにくせがあり、操作になれるのがちょっと大変です。でもその操作に慣れてしまうと、すばやく作業ができるようになります。(らしいです)

しかし、いきなり複雑なモデルにするとデバッグも大変なので、とりあえず人の腕っぼい関節がひとつだけの簡単なモデルにして、やってみることにしました。といってもほとんど直方体と変わらないですが...

あと作ったモデルをプログラムで利用するために、データとして保存しなくてはなりません。Blenderの保存形式に合わせて解析するのは大変なので、自分の使い易い形式に保存することにします。そのためには、Blenderが備えているPythonのスクリプト機能を使うことになります。

続きはまた...

2009年6月14日日曜日

ボーンアニメーション

日記を書くのをだいぶサボってしまいましたが、その間、いろいろ勉強してました。

3Dのこと、AIのこと、Objective-C(←iPhoneアプリに触手を伸ばしたり...)のこと等々。

で、勉強もいいけど、実際に作ってみないとやっばり身に付かないなぁということも感じたり...

つうことで、とりあえず、自前でボーンアニメーションを実装してみようかなと。。

でもその前にまず理論を押さえとかないと(やっぱ勉強からかT_T)ということで、

こことかこことか読んでみました。

いろいろ小難しいんですが、要は、ボーンを動かしてキャラクタのポーズをつけて、各頂点とそれらのボーンを関連づけて、ボーンの動きに頂点を追随させようというのが基本のようです。

これはなんとなく理解できます。ただそれだけだと、ポーズによっては、違うボーンに関連付けられた頂点同士の位置がずれて、面がくしゃってなったり、亀裂がはいったりします。そこで登場するのが、頂点ブレンディングという考え方です。これも一見複雑なんですが、要は、1つの頂点をボーンに関連付ける時に重みづけしましょうということのようです。例えば人間のキャラクタの肘の部分は、上腕と下腕の両方の動きの影響を受けます。この場合の肘の部分の頂点を上腕のボーンに0.5、下腕のボーンに0.5といった重みをつけて関連づけます。上腕に0.6、下腕に0.4といった関連付けでもOKですが、関連付ける重みの合計が1.0(=100%)になるように設定します。

うまく重みを設定してやると、腕を曲げても自然な感じで曲がってくれます。

最初は理論的なことや実装方法等がいまいち飲み込めなかったのですが、ふとした時にDirectXの「Skinned Mesh Character Animation with Direct3D 9.0c」というPDFの資料を見つけました。これの説明がかなり分かり易くて、もやもやしてたものが大分はっきりしてきました。DirectXの資料なのでもちろんXFileに関した話が中心なんですが、理論的な話についてはXFileに特化してるわけではなく汎用的な話としてかなり参考になりました。

ということで、ひとまず基礎的なところは理解しました。...たぶん