最近日記書いてないのは何もしてないからではなく、いろいろしてるから書く暇がない。いいのかわるいのか。
RubyでGLSLパーサ書いてる続き。
プリプロセッサの処理がめんどくさくて適当に処理した方がいいかと思ったがそうでもなかった。
普通に上から実行していけばいいだけじゃん。ディレクティブ以外のトークンはマクロ展開して出力すればいいのか。
パーサジェネレータはTreetopっていうRuby用のPEG解析器(?)。PEGってのは正規表現のすごいやつっぽい感じ。いやStringScanner+Regexpのすごいやつって感じか。
PEG自体は書き方を理解してしまえばそれほど難しくはない。 しかしTreetopのランタイムが微妙に使いづらい。
基本的にはRaccと同じように文法ファイルにRubyコードを埋め込めるんだが、PEGだから繰りかえしみたいなのを指定した時に綺麗に処理できないとかめんどい。
うーん、でもめんどさ的には実はかわらんかなぁ。
あと文法ファイルでSyntaxNodeのサブクラスを指定できるよ!って書いてあるから指定してみたらmoduleじゃねーとダメだ的なことを言われた。うそつき…。うそじゃなくてあとで変更されただけなんだろうが。
珍しくテストを書きながら進めているので、なかなか進まない。テストの書き方が分かってないから慣れていかないとな。
WriteableBitmap#RenderにTransformGroupを渡す時は注意って話。
WriteableBitmap#RenderってUIElementとTransformを渡すと、UIElementをTransformつかって変形した場所に描画してくれるんですよ。まんまですね。
で、こんなコードを書いたわけですよ。
##highlight ruby bmp = WriteableBitmap.new(640, 480) transform = TranslateTransform.new draw_list.each do |pos, rot, image| transform.x = pos.x transform.y = pos.y bmp.render(image, transform) end bmp.invalidate
IronRubyだけど。draw_listには描画したいイメージと位置と向きの配列を入れてあります。でも向きはこの時まだ使ってません。
ポイントはtransformを使いまわしてるところ。これはちゃんと動くんですよ。まあrenderで逐一描画されてるイメージ。 最後のinvalidateは描画確定で、これ呼ばないと反映されません。
このあと、回転もしたくなったのでRotateTransformを追加するんだけどtransformは一個しか渡せないのでTransformGroupでまとめる。
##highlight ruby bmp = WriteableBitmap.new(640, 480) transform = TransformGroup.new rotate = RotateTransform.new translate = TranslateTransform.new transform.children.add(rotate) transform.children.add(translate) draw_list.each do |pos, rot, image| translate.x = pos.x translate.y = pos.y rotate.angle = rot bmp.render(image, transform) end bmp.invalidate
やっぱりそれぞれ使いまわします。transformの子だけいじるのは微妙に気持ちわるいけど。
しかしこれが正しく動いてくれない。全部のイメージが同じ場所に描画されてしまいました。
さて何が起きてるのか。
現象からすると、描画はWriteableBitmap#Renderを呼んだ時点で行なわれてるとは限らず遅延されてる模様。
ではなぜ前者は正しく動いた(もしくはそう見えた)のか。これはおそらく渡したtransformがコピーされてるんだろう。
コピーされてるにもかかわらず後者が正しく動かないのは、浅いコピーであってTransformGroupの子まではコピーされないからだと思われる。そのため子のメンバーを書き換えると遅延されてた描画が影響を受けてしまう。
まあたぶんこんなところでしょう。
じゃあどうやって解決するか。
渡すtransformをつかいまわさず描画毎に生成というのは妥当な解決方法だけど、100個とか200個とか描画したいのでちょっと抵抗がある。
それよりTranslateTransformだけを渡していた時はコピーされていたらしく正しく動いたので、それと同じくTransformGroupを使わなければいいわけだ。
ということでMatrixTransformを使えば解決できるはずだけど回転の中心も変えたいので行列作るのがちょっとだけめんどい。そこでSilverlight4からはCompositeTransformってのが追加されてたのでこれを使った。
CompositeTransformは1つで拡大縮小、回転、移動を一気に設定してやってくれる変形で、まあ大抵の用途はこれで十分である。 1つのオブジェクトで全部やってくれるので使いまわしても問題は出なかった。
えーと、結論としては…。WriteableBitmapは描画遅延してるらしいよっていうのと、Transformつかいまわすときには気をつけろてこった。
Silverlightってできる限りブロックしないようにものすごい気を使って作られてるなぁ。そう考えるとWriteableBitmapでブロックしないのも当然か。
あんまりにも非同期でしか動いてくれないのもゲームとしてはもどかしい所があったりするんですが。