2008年4月26日土曜日

blender - ファイルフォーマットを書き換えたい(18)

現状。
がんばってキーをかなり完璧に吐けるようになりました。
まだ法線とか位置とかは直してないです。特に法線が変なせいで、口元が変に光ってキモくなってますorz

動画は順番に、

1.△21966ポリゴン(subsurf)、テクスチャ、頂点カラー、一部マテリアルカラー、
  どのボーンも同じタイミングのキーがある。
2.△4917ポリゴン、以下同じ。ただし、キーを少し追加したため、右足が少しマシ。
3.△26000ポリゴン(subsurf)、テクスチャ、頂点カラー、マテリアルカラー
  ボーンによってキーのタイミングが違う&ほとんどキーが無いボーンもあり

となってます。

少し音出るので注意。



どれも、75fpsフルで出てます。(うちのモニタは75hzなので多分最大。)
固定機能なので旧MacMini@GMA950でも動いて、そちらのモニタの最大値60fps出てます。1と2はボーンが115本もあるので、結構ヤバイかと思ってたんですが、1体くらい余裕ですね~。

3のアニメーションは、まだちょっと怪しいんですが、原因は、キーを吐く順番がちょっとずれたとか、多分そんなの。

blender - ファイルフォーマットを書き換えたい(17)

ついに軸が直りました!!!

軸がっていうか、全体的におかしかったんですが…。
ようやく頂点ブレンディングの座標変換が分かってきて、何とかなりました。



Blenderの座標系は

MeshObjectの座標系(頂点データがある) -> MeshのObjMat -> View(いつもモデリングしてる画面)の座標系 -> ArmatureのObjMat -> ArmatureObjectの座標系 -> ArmMat -> Editモードでの姿勢 -> ある時間でのPoseChannelのPoseMat -> あるボーンの姿勢 -> (*)

といった感じで、かなりややこしいです。アニメーションさせるときは、矢印(->)を全て逆に、つまり各種変換行列を逆行列にし、 (*) 部分に、さらに親のボーンの行列をRootまで掛けたものが入ります。

そのうち図にして、解説サイトでも作ろうかと思ってます。ちなみに、まだBlenderの座標系(左手系のyとzが入れ替わってるやつ)が全然分かってません。適当にやってたら出来たってレベル。まぁ出来ればいいってとで…。

---

まだ法線が変だったりしますが、BlenderのPoseシステムは、(多分)IKまで計算してくれてるようで、ミクも普通に動いてます。次は、ちゃんと確実にBlenderのキー時間を取得して吐く予定。これはBlenderのソース漁りまくってたら、それっぽいコードを見つけたので多分すぐ出来る。あと、行列キーじゃなくて、移動、回転、拡大縮小のキーで、うまく補間したアニメーションできるようにしたい。

2008年4月25日金曜日

blender - モデリング再開

今週までクソ忙しかったんですが、ようやく落ち着きました。
さっそくフル稼働でやっていきたいと思います!

まずはモデリング再開。とりあえず前一瞬作りかけたレナから1時間ほど。うーん、レナって良く知らんのんだけど、元の絵がへb…いや、なんというか、人によって全然違うので難しいなぁ。(かわいいけど、レナって言われないとレナに見えない絵が多いような)


目の色含め、全体的にまだまだしょぼい。っていうか顔orz

------

ファイルフォーマットのは、アニメーションがどうしても上手くいかなかったので、標準のXエクスポーターと細かく比較しながら、Xファイル吐いてみて、またも軸がぶれぶれぶれぶれ。で、長い格闘の末「そもそも頂点の座標が違う」ということに気づきました。標準のやつでは、Pose適用済の座標が吐かれていました。

しかし、Pose適用済頂点にすると、初期位置がそれになってしまうので、多分実質1つしかアニメーション付けれません。やはり1メッシュ1Armatureに複数アニメーションセットが理想です。というわけで、先は見えつつあるものの、まだまだかかりそうです。

そもそも、まともに複数アニメーション吐けるエクスポーターってあるのかな?

2008年4月15日火曜日

etc - 人として軸がぶれている

下宿に帰りました。


http://xoomer.alice.it/glabro1/python241.html
早速、ここのデータをコンバートしてみたところ。



ちょっと軸がぶれているのが直りそうも無いので、Loc/Scale/Quaternionをそれぞれ分けて合成するのではなく、行列をぶち込む方法にしてみようと思います。

あと、ボーンの行列(Xfileで言うところのFrameTransformMatrix)が違うのかと思って、先ほど必死に上記URLの標準搭載されてるエクスポーターと同じものを吐くようにしてみたんですが、いろいろ検索したりコード見た結果、スキンメッシュだとFrameTransformMatrixが要らないことが分かってショックでした。

---------

ついにウェブカメラ買ってしまった。2000円。





ついでに江波団長が作ってたルナ@瀬戸の花嫁。

2008年4月12日土曜日

etc - また東京来てます


たまには2D模写。しばらく何も書いてないと、一筆で書けるストロークの長さが確実に減っていって、2cmくらいになる。と言っても3Dモデリングには問題ないんだけど、なんか気晴らしに書いてみた。胸の辺がおかしいな・・・


前買った、雫組の同人ゲーム。初音ミクの「みっくみくにしてやんよ」の3DPVが有名ですね。
いやー、凄かったです。LightWave + AfterEffectうらやましい。


またしても秋葉原駅前のBeckersでバーガー。
前食べ損ねた200gのを注文。単品で600円、セットで900円ちょい。

2008年4月8日火曜日

blenderXML - ドキュメント制作

そろそろドキュメントも書き始めないとなぁ、、ということで、少し書きました。

http://tori31001.googlepages.com/blendertodirectx


Google Pagesを使ってみました。若干重いけど、かなり良い感じ。

気づいたら、GooglePages、Gmail、Blogger、iGoogle、GoogleReader、GoogleBookmark、GoogleAnalyticsと、完全にGoogleユーザーになってしまっています。特にGoogleReaderとGmailは手放せません。

2008年4月7日月曜日

blender - ファイルフォーマットを書き換えたい(16)

…長い!!なんて長い道のりなんだ。
ゲーム業界の登竜門と言われてるだけあるな…。
どのくらい長いかは、ここ参照。(このサイトが無かったら出来てないかも…)
コンバーターからやってるので、読み込むまでが長かった。
それから、読み込んでからも、色々読んでは試して、失敗して、ぐちゃぐちゃなモノが表示されて…悲しくなってくるのでこの辺にしておこう…。

というわけで、何とかボーンで、IPOのLOCは動くようになりました。



元の.blendファイルはこちら
http://render.jp/blender/locTest.blend


あとは、SCALE(SIZE)とQUATERNION。

BlenderのIPOはBAKEなしで、キー1つあたり4曲線で補間、BAKEすると1直線(1曲線)になるっぽいな。


追記:SCALE(拡大縮小) + QUATERION(回転)
ついでに、座標系を直した。


Blenderの座標系は多分こんな感じ。


次は、初音ミク+BAKE なウマウマ。しかしどうも上手くいかない。
どうも初期位置がおかしい気がする。

2008年4月4日金曜日

etc - Java SAXパーサーで任意のトークン切り出し

SAXパーサーでは中途半端な文字数ずつ渡ってくるので、前回の最後の、指定した個数に満たなかった分を
内部に保持しておかないといけない。なので、StringTokenizerやString#splitでは何とかならない。

ちょっとググって無かったので、適当に作ったのを適当に載せときます。メインの部分が汚ねぇ…。

Java SAXパーサーから渡ってくるテキスト要素を、任意のトークンで、指定した個数ずつ切り出すクラスです。指定した個数ずつ読み込むけど、途中で個数変えるのは想定してないです。

ここまで作っといて言うのも何だけど、メモリなんか気にせず、DOMでまとめて読み込んだほうが圧倒的に楽。



/**
* SAXTokenizer.java
* @author uimac
*/
import java.util.Iterator;

interface SAXTokenizerIterator extends Iterator {
public abstract boolean hasNext(int n);
public abstract boolean hasNext(int n, char delim);
public abstract boolean hasNext(int n, char[] delims);
public abstract Object[] next(int n);
public abstract Object[] next(int n, char delim);
public abstract Object[] next(int n, char[] delims);
}

public class SAXTokenizer implements SAXTokenizerIterator {
private final char[] delims;

/**
* Creates a new instance of SAXTokenizer
*/
public SAXTokenizer(char[] delims) {
this.delims = delims;
}

private char[] ch = null;
private int offset = 0;
private int length = 0;
private int index = 0;
private StringBuffer buf = new StringBuffer();

private String[] res = {""};
private int res_index = 0;

// このメソッドでSAXで渡ってくる文字列を毎回設定する
public void setChars(char[] ch, int ofs, int length) {
this.index = ofs;
this.ch = ch;
this.offset = ofs;
this.length = length;
}

public void reset() {
this.index = 0;
this.offset = 0;
this.length = 0;
this.ch = null;
this.buf = new StringBuffer();
this.res_index = 0;
this.res = new String[1];
this.res[0] = "";
}

// 次のトークンがある場合に true を返します。
public boolean hasNext() {
return hasNext(1, this.delims);
}

// 次のトークンがn個ある場合に true を返します。
public boolean hasNext(int n) {
return hasNext(n, this.delims);
}

// 次のdelimで指定したトークンがn個ある場合に true を返します。
public boolean hasNext(int n, char delim) {
char[] delims = { delim };
return hasNext(n, delims);
}

// 次のdelimsで指定したトークンがn個ある場合に true を返します。
public boolean hasNext(int n, char[] delims) {
int count = 0;

for (int i = this.index; i < offset + length; ++i) {
for (int k = 0; k < delims.length; ++k) {
if (ch[i] == delims[k]) count++;
if (count == n) return true;
}
}

return false;
}

// セットされているdelimsトークンで切り出した文字列を、1個返す
// 無かったらnullを返す
public Object next() {
return next(1);
}

// セットされているdelimsトークンで切り出した文字列を、n個返す
// n個無かったらnullを返す
public Object[] next(int n) {
return next(n, this.delims);
}

// delimトークンで切り出した文字列を、n個返す
// n個無かったらnullを返す
public Object[] next(int n, char delim) {
char[] delims = { delim };
return next(n, delims);
}

// delimsトークンで切り出した文字列を、n個返す
// n個無かったらnullを返す
public Object[] next(int n, char[] delims) {
// サイズ違うので、新たに返り値用文字列バッファ生成
if (res.length != n) {
res = new String[n];
}

// 前回読み込んだところ以降を、空文字で初期化
for (int i = res.length - 1; i >= res_index; --i) {
res[i] = "";
}

boolean isOut = false;
for (; index < offset + length; ++index) {
for (int i = 0; i < delims.length; ++i) {
if (ch[index] == delims[i]) {
String str = buf.toString();
buf.delete(0, buf.length());

if ( (index+1) != offset + length)
++index;
else
isOut = true;

if ( !str.equals("") ) {
res[res_index++] = str;
}

if (res_index == n) {
// 全部そろったので返す
res_index = 0;
return res;
}
}
}

if (!isOut)
buf.append(ch[index]);
}

// 指定した個数に満たなかった
return null;
}

// サポートしてないです
public void remove() {
throw new UnsupportedOperationException();
}

}


使用例


// トークン切り出し用オブジェクト。トークンは複数指定できる。
private SAXTokenizer tokenizer = new SAXTokenizer(",;".toCharArray());
// 要素読み込み中フラグ。
// 被るのがあるときはStackにしたり、フラグ増やしたりしないといけない。
private boolean tagA = false;
private boolean tagB = false;

// SAXの要素タグ読み込みメソッド。
public void startElement(String uri,
String localName,
String qName,
Attributes attributes) {

tagA = qName.equals("tagA");
tagB = qName.equals("tagB");
// 要素毎に読み込むため、ここでリセットかけとく。
tokenizer.reset();
}

// SAXのテキスト読み込みメソッド。中途半端な文字数ずつ渡ってくる。
public void characters(char[] ch,
int offset,
int length) {

// 例1:もともとセットされてるトークンで、3個ずつ切り出し
// 元データは例えばこんな感じ 111,222,333;44,5555,66666;77,88,999; ...
if (tagA) {
tokenizer.setChars(ch, offset, length);
for (String[] nums = (String[])tokenizer.next(3); nums != null; nums = (String[])tokenizer.next(3)) {
// なんか処理とか。ここで、numsには必ず3個入っている。
System.out.println(nums[0] + "," + nums[1] + "," + nums[2] + ";" );
}
}

// 例2:トークンを新たに指定して1つずつ切り出し
// 元データは例えばこんな感じ 111,222;333;444,555,666;77,88;999 ...
if (tagB) {
tokenizer.setChars(ch, offset, length);
for (String[] blocks = (String[])tokenizer.next(1, ';'); blocks != null; blocks = (String[])tokenizer.next(1, ';')) {
// 切り出した文字列から、さらにトークンで分割。StringTokenizerよりsplit使えってSUNが言ってた
// http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/StringTokenizer.html
String[] nums = blocks[0].split(",");
// なんか処理とか。ここで、numsには必ずnums.length個入っている。
for (int i = 0; i < nums.length; ++i) {
System.out.print(nums[i] + ",");
}
System.out.println("\n");
}
}
}


あ、あと、ここまで実装しといて言うのもなんだけど、
hasNext()で終了判定すると、最後の切れ目の部分が読み込まれないので、返ってくるのがnullかどうかで判定してます。ここを何とかしたいんだけど、まぁこれはこれでhasNext()が意味通りなので良いか。

2008年4月2日水曜日

blender - ファイルフォーマットを書き換えたい(15)

長らく更新が途絶えてすいません。各地を飛び回ってて全然進んでません。
何か書かないとやる気が減る一方なので、出来てないけど現状書いてみます。

何を出力すればいいのか、また、どう実装すればいいのか、分からないので、
手探りで、大量の数字とにらめっこしてます。
来週の火曜までに出来なかったら、夏までのスケジュール的にもヤバ目なのでがんばります。

現状、ボーンとIpoを吐いてみたんですが、Ipoは1つのキーにつき、4つの線で補間されていて、これから計算するのは面倒なので、しょうがないから4つのうちの最初の1つだけを吐いてみたのがこちら↓

何でIpo吐いてるかというと、キーフレームのほうの吐き方がいまいち分からないからです。先人のスクリプトによると、キーフレーム指定して全部更新しなおせば、ポーズの中に計算された行列が入ってるらしいんですが、重そうだし、そもそもキーがどこにくっついてるのか分かってないです。さし当たって、これでがんばってみて、駄目だったらゲームエンジンの実装でも参考にしようかと思ってます。

あと、このIpoの中身の数字って、どの座標系の数字なんだろう?

早く完成させたい気持ちと、自分の能力が足りなくて中々出来ない現状は、もどかしいですね~。
でもこれが出来て、さらにノードとかカメラワークとかも吐いて、DirectXと連携すれば、Blenderは相当イイ感じのゲーム用3Dツールになるはず!