2007年10月24日

MDDファイルをPythonで扱うために その3 modo 301

昨日今年初の灯油販売のトラックがやって来たから灯油がいくらになったか聞いてみたら、

ナント1600円!!

しかも来月には値上がりするらしい・・・((((;゚д゚)))ガクガクブルブル

灯油泥棒も出るわけだな。外に灯油タンクがある家は気をつけた方がいいよ。それと、Pen4からcore2に乗り換えようと思っている人、冬場は暖かいCPUがグッドよwww。

さて、前回はバイナリーファイルを扱う基本をやったので、今回はそれを処理してMDDファイルを読み込む方法をやってみよう。

まず確認してほしいのは、MDDファイルの構造ね。これはその1でもやったけど、もう一度表を掲載しておくよ。

フィールド名 データ型 サイズ 内容
fmax
int
4 bytes
MDDファイルの最大フレーム数
pmax
int
4 bytes
パーティクルの最大数
time[fmax]
float
fmax * 4 bytes
フレームの時刻
org[pmax]
float[3]
pmax * 12 bytes
元オブジェクトのポイントの座標値
data[fmax][pmax]
float[3]
fmax * pmax * 12 bytes
各フレームごとの座標移動データ

ここでまず注目してもらいたいのがデータの型とサイズだ。型としてintとかfloatとか書いてある。これらはC言語の型なんだけど、それぞれ数値を扱うのために、特別な決まりに従って2進数に変換されて格納されるようになっている。だからファイルから生のデータを読み込んだ時に逆変換して、数値に戻さないといけないって話は前回したよね。ただしその変換方法はかなり複雑だって。で、それを解決するお手軽ライブラリとしてarrayがあるってところで前回は終わった。

そこでとりあえずarrayモジュールがプログラムの中ですぐに使えるようにしておこう。プログラムにarrayモジュールをインポートする。

from array import array

普通にimport arrayって書いてもいいんだけど、そうするといちいちarray.arrayみたいに書かなくちゃならなくなるけど、こう書いておけば最初のarrayを省略できるようになるよ。

さて、arrayがインポート出来たら、いよいよarrayの説明に移ろう。

arrayの特徴は、リストなどのコンテナと同様に、複数の値を順番に格納出来ることと、その格納できるデータの種類がarrayひとつに対してひとつと限定されている事だ。そしてその種類はarrayの初期化の段階で決められて、都合のいいことに、バイナリーファイルから、直接その型のデータとして読み込んで変換する機能が搭載されている。この事さえ知ってしまえば話は半分以上解決したようなもんだ。MDDファイルに格納されている各データにあわせたarrayを作って、それに開いたファイルを読み込ませるだけで自動的にバイナリー数値変換も終わっちゃうわけだもんね。そこで上の表を改めて見直すと、数値を表現する型は4バイトのintと4バイトのfloatだけだ。arrayは初期化の時に型を型コードという文字で決定するようになっていて、その対応は以下の表の通りだ。

型コード C の型 Python の型 最小サイズ (バイト単位)
'c' char 文字(str型) 1
'b' signed char int型 1
'B' unsigned char int型 1
'u' Py_UNICODE Unicode文字(unicode型) 2
'h' signed short int型 2
'H' unsigned short int型 2
'i' signed int int型 2
'I' unsigned int long型 2
'l' signed long int型 4
'L' unsigned long long型 4
'f' float float型 4
'd' double float型 8

これで見ると4バイトのintはsigned long(±符号付)で型コードは'l'、4バイトのfloatは'f'だ。だから最初の最大フレーム数と最大パーティクル数のfmax,pmaxを取得するarrayは、

armax=array('l')

なんて形で初期化して作成すればOK。ファイルを開いてファイルアクセスオブジェクトが変数fに設定されているなら、そこから一気に、

armax.fromfile(f,2)

ってやってやれば、先頭から8バイトを2つのint型の数字としてarmaxに読み込んでくれる。あー便利。ここからfmaxとpmaxを取り出そうと思ったら、2つの数値はarmaxに順番に格納されているので添字を付けてこのようにアクセスしてやればいい。

    fmax=armax[0]
    pmax=armax[1]

なんだけど、ここでひとつ問題が生じる。実際やってみるとおかしな数値になってしまう。それはunsigned intを表現するビットのフォーマットはひとつなんだけど、コンピュータは8ビットをひとつの区切りとして扱っていたために、複数のバイトにまたがってビット列を格納する方法がいろいろ存在するからなんだな。そのうち代表的なのがビッグエンディアンとリトルエンディアンという方式で、言うならば英語の文は左から右に向けて書くけど、アラビア語は右から左に向かって書く(意味がわからん奴は「紅の豚」の冒頭でも見てくれ)、あの違いみたいなもんだ。さっきも書いたようにビットは8つで区切って記録していて、今回の4バイトint型は32ビットを4つの8ビットの並びで表現している。その4つに区切ったそれぞれを、アドレスの小さい方から順に格納するのか(ビッグエンディアン)、それともアドレスの大きい方から格納するのか(リトルエンディアン)、それがこれら2つのエンディアンの違いになってくるわけだ。例えば 12 34 56 78 という4バイトのデータをビッグエンディアンで格納すると、アドレスの小さい方から12 34 56 78という順番に格納され、リトルエンディアンでは、78 56 34 12という順に格納されるわけだ。だからこの方式が異なると、せっかくビット列から数値に変換する仕組みを持っていても、役に立たないわけだ。しかしラッキーなことに、arrayにはその変換をしてくれるメソッドbyteswap()があるんだな。だからfomfile()メソッドでファイルからデータを読み込んだら、そのarrayに対してbyteswap()を1回するだけで問題解決だ(すげー)。

armax.fromfile(f,2)
armax.byteswap()
fmax=armax[0]
pmax=armax[1]

これで無事にfmaxとpmaxを取得できる。残りのデータは、fmaxとpmaxから数がわかるので、その数ぶんだけarrayにfromfile()メソッドで読み込んでやればいいし、座標データはx,y,zの3つの値でワンセットなので、arryaから数値を読み出して、リスト型のコンテナ上に再構成してやればいいだろう。

以下はmddファイルを読み込んでイベントログパネルに出力するプログラムだ。試すmddファイルが無かったら、以前にアップした「MDDアニメーション やってみた」のデータを使ってみてくれ。

#python
import sys
import lx
from array import array

lx.eval('dialog.setup fileOpen')
lx.eval('dialog.title "Select MDD File"')
lx.eval('dialog.result "*.mdd"')
armax=array('l')
artime=array('f')
arorg=array('f')
ardata=array('f')
lx.eval('dialog.setup fileOpen')
lx.eval('dialog.title "Select MDD File"')
lx.eval('dialog.result "*.mdd"')
try:
    lx.eval('dialog.open')
    mddfile=lx.eval('dialog.result ?')
    f=open(mddfile,"rb")
    armax.fromfile(f,2)
    armax.byteswap()
    fmax=armax[0]
    pmax=armax[1]
    artime.fromfile(f,fmax)
    artime.byteswap()
    time=artime.tolist()
    arorg.fromfile(f,pmax*3)
    arorg.byteswap()
    ardata.fromfile(f,fmax*pmax*3)
    ardata.byteswap()
    f.close()
    org=[]
    for i in range(pmax):
        org.append([arorg[i*3],arorg[i*3+1],arorg[i*3+2]])
    data=[]
    for f in range(fmax):
        pdata=[]
        for p in range(pmax):
            pdata.append([ardata[(f*pmax+p)*3],ardata[(f*pmax+p)*3+1],ardata[(f*pmax+p)*3+2]])
        data.append(pdata)

    lx.out("fmax=",fmax)
    lx.out("pmax=",pmax)
    lx.out("time[]=",time)
    lx.out("org[]=",org)
    lx.out("data[]=",data)

except IOError:
    lx.out(sys.exc_info()[0])
    lx.eval('dialog.setup error')
    lx.eval('dialog.title "Open Error"')
    lx.eval('dialog.msg "ファイルが開けませんでした"')
    lx.eval('dialog.open')
except RuntimeError:
    lx.out(sys.exc_info()[0])

それではまた次回。

スクリプトまとめページ( Down Load はこちらから)  

カテゴリー別ページ

modo操作メモ 



take_z_ultima at 12:34│Comments(0)TrackBack(1)modo | CG

トラックバックURL

この記事へのトラックバック

1. Array  [ 一語で検索 ]   2007年11月10日 13:35
Arrayでの検索結果をマッシュアップ。一語から広がる言葉のポータルサイト。

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 

Archives