なんとなく思い付いて ID を取得してやってみたんですが、 XML のパースで失敗するっぽい。
gosh> (use rfc.http) gosh> (define-values (status header body) (http-get "webservices.amazon.co.jp" "/onca/xml?Service=AWSECommerceService&SubscriptionId=0D8NM7HNWSQBRZTMGY02&AssociateTag=torusjp-22&Operation=ItemLookup&ItemId=4101183147"))
HTTP のリクエスト自体は正常に動いて、 ちゃんとレスポンスが返ってきます。 あ、でも、body が不完全文字列になってます。
gosh> body #*"<?xml version=\"1.0\" encoding=\"UTF-8\"?> ...
そして、
gosh> (use sxml.ssax) gosh> (use srfi-6) gosh> (define sxml (ssax:xml->sxml (open-input-string body) '()))
こうすると、エラーがでます。
*** ERROR: "(input string port)":line 1: reading char data Stack Trace: _______________________________________ 0 (ssax:xml->sxml (open-input-string body) '()) At line 11 of "(stdin)"
だれかうまくいった人いないかなぁ。
open-input-conversion-port を使って内部エンコーディングに変換したらうまくいきました!
はずかしいけどとりあえずリリース。 あとでメーリングリストにもだしてみよう。 http://sourceforge.jp/projects/mephisto/
まだドキュメントがぜんぜんありませんが、 Gauche-gl があれば動きます。 アーカイブを展開して、 examples のところの live-human.scm というのを実行すると、 IPAX でやったクレイジーなデモがあらわれます。
実行中に動的に制約をどんどん追加して、 動作が変わっていくところのデモです。 本当は成果報告会の時にこれをやりたかったんだけど, へんなところにはまってしまって全然間に合わなかった……。
Vnc2swf を使って録画しました。 実際の画面は 40 fps 以上でけっこう滑らかに動いているんですが、 この映像はガクガクです。 これは VNC の限界かなぁ。 いろいろ加工すればもっと滑らかな映像になりそうだけど。
さいきん cut というのを覚えたので、 いままで lambda をたくさん書いていたのをちょこちょこ直しているんですが、 <> はどこにでもかけるって訳じゃないんですね。 ここでだいぶ長いことはまってしまった。
gosh> ((cut not (null? <>)) 1) *** ERROR: Compile Error: wrong number of arguments: #f requires 0, but got 1 "(stdin)":10:((cut not (null? <>)) 1)
「unbound variable: <>」とかいわれていればすぐ気付くんだろうけど。 でも、マクロの仕組みを考えれば当り前だなぁ。
あとうっかり (cut #?=<>) とかやってはまりそう。
テキスト形式の 3 次元モデルのファイルフォーマットで、 よく使われているっぽい。 フリーの素材もいろいろ出てきました。
フォーマットの仕様はここで見つけました。 Wavefront OBJ: Summary from the Encyclopedia of Graphics File Formats
なので、これを読み取って Gauche-gl の手続きに変換する関数を書いてみた。
(define (read-obj-model path) (let ((vertices '(v)) (polygons '(p)) ) (define (add-vertex v)(append! vertices (list v))) (define (add-polygon p) (append! polygons (list p))) (with-input-from-file path (lambda () (port-for-each (lambda (line) (let1 line (string-trim-right (or (string-scan line #?# 'before) line)) (unless (string=? line "") (match (string-tokenize line) (("v" coord ...) (add-vertex (list->point4f (map obj-value->number coord)))) (("f" vtx ...) (add-polygon (map obj-value->number vtx))) ((sym args ...) (print #`"unknown symbol ,|sym|: ,args")) )) )) read-line)) :encoding "shift_jis") (lambda () (map (lambda (poly) (gl-normal (vector4f-normalize (vector4f-cross (point4f-sub (ref vertices (ref poly 2)) (ref vertices (ref poly 1))) (point4f-sub (ref vertices (ref poly 3)) (ref vertices (ref poly 2))) ))) (gl-begin GL_POLYGON) (map (lambda (i) (gl-vertex (ref vertices i))) poly) (gl-end)) (cdr polygons)) ) ) ) (define (obj-value->number x) (with-input-from-string x read))
ECMAScript ベースの言語のコンパイラで、 専用の VM 用のバイトコードだけじゃなくて、 Flash(SWF)やウェブブラウザ用の JavaScript のコードも出力できるそうです。
そして OCaml で実装されているらしい。
だれか使ってる人いないかな。
Emacs から gosh を呼び出して、 Gauche-gl のコードを実行するとき、 glbook の例では ESC キーを押すと (exit 0) を実行するので、 Emacs 内の gosh のプロセスもろとも停止してしまいます。
なので、ESC キーを押したら main 手続きから返るようにこんな風に書いてみたら、 だめだった。
(define (make-keyboard-func cont) (lambda (key x y) (cond ((= key 27) (cont 0)) ))) (define (main args) ; ... いろいろと初期化 ... (call/cc (lambda (cont) (glut-keyboard-func (make-keyboard-func cont)) (glut-main-loop))) 0 )
たしかに main 手続きから返ってはくるんだけど、 OpenGL まわりの終了処理が走らないのでへんなことになっちゃいます。 glut-main-loop はそもそも途中でループを抜けないという前提で作られているのであたりまえか。
glut-main-loop が回っている間に、 一部の手続きの定義を変更して実行時に動作を変化させることってできないかな。
(thread-start! (make-thread (lambda () (read-eval-print-loop)))) (while #t (guard (e (else (report-error e))) (glut-main-loop)))
■ 逆方向キネマティックスによる巨像の動き制御
最も単純な3Dゲームにおけるキャラクタの移動は、プレーヤーの入力などから次の位置情報を算出して、移動モーション(走る姿とか)を再生しつつそこへ動かす……といった処理系になる。こうした3Dゲームでは、なんというか地面の上を滑るようにして走っているように見えてしまう。
「ワンダと巨像」に登場する巨像はとにかくでかく、プレーヤーは、巨像をクローズアップで見ることになるので、単純な方法で動かしては不自然さが確実に露呈してしまう。巨像の動き制御には文字通り「地に足のついた」踏み出して歩くような動きが必要となる。
あああ、ぼくがやりたいのはまさにこれのことです。 「ワンダと巨像」まだやってないんだけど、買うしかないか。
こういうのはメインプログラマ以下、意志統一がされてないとなかなか難しい。「ワンダと巨像」では、垂直帰線期間を見て余裕があるときに、 バックグラウンドでこうしたメモリ領域の再配置処理を行なっているのだという。
SICM をぱらぱらめくっていて初めて知ったんですが、 手続きを返す手続きってこんなに簡単に書けるんですね。
gosh> (define ((compose f g) x) (f (g x))) compose gosh> (define (square x) (* x x)) square gosh> ((compose square sin) 2) 0.826821810431806
マクロの書き方をちょっと鍛えて、 こんな風に書けるようにしてみた:
(define (make-multiplier) (make-constraint (m1 m2 product) ((m1 m2 => product) (* m1 m2)) ((m1 product => m2) (/ product m1)) ((m2 product => m1) (/ product m2)) ))
前と違うのは、矢印「=>」の向きです。
((m1 m2 => product) (* m1 m2))
というのは、m1 と m2 が値を持っていれば product に m1 と m2 の積の値を割り当てるという意味です。
んー、もうちょっとがんばれば、
((dividend divisor => quotient remainder) (quotient&remainder dividend divisor))
みたいに書けそうだなぁ。 でも多値が出てくると却って面倒になるかな。
とりあえず書けたので、竹内先生にも送ってみた。 Source Forge に置いてありますので、よかったら見てください。 PDF ファイルです。
http://cvs.sourceforge.jp/cgi-bin/viewcvs.cgi/mephisto/misc/prosym2006/
んー、Scheme の話はぜんぜん出てきてないな。 っていうか、まだちゃんと実装できてない部分があるので、 いまいち具体性に欠けるんだよなぁ。
でもなんとなく当日のデモの内容とかもイメージが出来てきたかも。
ツッコミありがとうございますー。 発表当日にはきちん答えられるようにしたいですが、 ちょっと未検証の部分があるので以下はいま漠然と考えていることです。
解が一意に定まらない時のために、 バネを入れたり物理計算をたくさんやるというのも一応考えてはいるんですが、 これらの対応の仕方は、 たぶんアプリケーションの性質やデザイナーの意向によって、 要求が変わってくると思うんですよね。 なので、 この辺の振舞はあとからいくらでも追加できるような作りにしたいと思っています。
とくに、いちばんやりたくないのがトレランスの値をつかう実数の比較で、 2 つの点を同一とみなすかどうかの判定などは、 アプリケーションの性質(CAD なのか、ゲームなのか、映画なのか、など)によって 全然やりかたが変わって来ますよね。
(2005/11/29 03:33:51 PST): コリジョンに関してはまだうまいやり方があるか分からないんですが、 コリジョンがおきた時だけ活性化して、値を上書きする制約 (もはや制約というよりは神経細胞みたいなイメージなんですが)とか、 そういうやり方ができたら面白いかなぁとか考えています。
マリオネットだとひざに糸がついていて、 糸を引き上げるとひざを上げるようなのが多いんですが (こんなの)、 これは足が地面につくと糸がたるんで、 ちゃんとぴったり足がつくようになっています。 こういう糸がたるむ感じをうまく制約で表現できるといいなぁと思ってます。
次世代の 3D シーンデータフォーマットということで、 SCEI とかが考えたやつですが、 少し前に仕様書をダウンロードして眺めながら様子見中です。 VRML をより現実に即した形で設計しなおしたっていう感じかな。 理想よりもゲーム制作の現場からの視点を重視して作られたっていう感じですかね。
SXML を使ってがんばれば、 COLLADA のデータから Gauche-gl の描画手続きへの変換は出来そう。 問題は安価なモデラがサポートするかどうかだなぁ。 Shade とかでエクスポートできるようになるといいなぁ。
Shade って Python でごにょごにょ出来るみたいだから、 がんばれば自分でエクスポータも書けるのかな。 でも自信ないな。
define-syntax の使いかたを覚えて、 だいぶすっきり書けるようになった。 たとえば、かけ算の制約はこんな風に書けます。
(define (make-multiplier) (make-constraint (m1 m2 product) ((product <= m1 m2) (* m1 m2)) ((m2 <= m1 product) (/ product m1)) ((m1 <= m2 product) (/ product m2)) ))
make-constraint で作った制約に、 値を保持するワイヤを接続することで、 制約伝搬を実現します。
あともうちょっといじったら公開しよう。
補助マクロを使った方法がやっと理解できた。 制約伝播を作るときにこの辺が分からなくて、 define-macro でごっちゃごちゃに書いていたので、 あとで define-syntax で書き直そう。
しかし、 define-syntax って分かると便利だなぁ。
単なる鏡像変換だと思って甘く見ていると、
ということに気づかずにはまりました。 そういえばこういうのを 擬ベクトル (pseudo vector) というのでした。 ベクトルの変換性に従わないベクトルとは似て非なるものです。
外積というとフレミングの左手の手をして考える人は要注意です。
gl.math3d に vector4f のスカラー倍の手続きがないのが不思議で、 他の人はどうやっているか気になったので、 そうだ Banyan を見てみようと思ってダウンロードしたら、 やっぱり動かしてみたくなったので、 いま MacOS X 上にいろいろインストール中。
gettext とかからインストールしないといけないのかー。 まだまだ先は長い。
点のアニメーションは、 時刻の集合 T から座標空間 S への写像としてかけます。
T → S
ある 3 次元モデルを N 個の点で表すとします。 つまり、モデルを点列
{P_i | i∈{0, 1, ..., N-1}}
のような形で書きます。
(一般に、形や方向を持つ剛体は、 それに含まれる 3 点の座標さえ与えれば、 状態(位置と向き)が一意に決まります。 なので、関節を持つ人型のモデルとかも、 たくさんの点を使えば状態(姿勢)を決定することが出来ます。)
とすると、 モデルのアニメーションは
T → S^N
という写像として表現できます。
さて、 ある決まった n 個の点の座標を与えると、 残り N-n 個の点の座標も決定するとします。 つまりこんな写像があるとします。
S^n → S^(N-n)
この写像を「制約」と呼ぶことにします。 (本来の意味からは非常に狭い意味になっちゃうけど。)
とすると、この n 個の点のアニメーション
T → S^n
を与えれば、 このモデルのアニメーションが決まります。
あれ、アニメーションと制約ってこんなに簡単な話なんだっけ?
っと思ったんですが、 これだけ簡単になったのは、 どこにも「角度」の話が出て来ないからかな。 角度を持ち出すと、 どうしても各パーツごとの局所座標系を考えざるを得なくなって、 座標変換行列の掛け算とかいろいろめんどくさくなります。 (というか、普通はそうすると思います。)
角度を 2 点を使って「方向」として表現(必要なら長さ 1 に正規化)すれば、 点だけの問題に還元できるので、 そしたら上に書いたような点と点だけの制約しか考えなくても、 モデルのアニメーションに関しては間に合いそう。
その辺に売っているモデリングソフトでも IK とかは普通に出来るけど、 アニメーションシーケンスのデータとして吐き出すときに、 制約についての情報を捨ててしまうので、 話がややこしくなってしまうんじゃないかな。 (昔ちょこっとだけ触ったのを覚えている限りでは、 RenderWare のアニメーションデータとか、 任天堂の NIFF とかいうデータ形式には制約についての情報はなかったと思う。)
SICP などに載っている制約解消系よりもだいぶ手抜きです。 ストリームを使ったダイナミックな変化と相性の良い制約の記述方法を考えています。
制約システムについては、 そのうちもう少し詳しい説明をつけたいと思います。
制約の表現はやっぱりまだ考え中。
MEPHISTO という、 Gauche-gl ベースの 3 次元 CG アニメーションの処理系を作ろうとしています。 2005年度未踏ユース に採択していただきました。
とりあえず、 8/27 の LLDN 夜の部でデモをやりますので、 チケットを持っている人はぜひ見てください。 ビール飲みながら見ると吐くかも(嘘)。
Scheme:Schemeプログラマのレベル10 を読んで、 せいぜいレベル 2.5 くらいなのに、 Scheme のプロジェクトを立ち上げてしまっているのに不安を感じますが。 まぁ、いいか。 Scheme の本は SICP(の途中まで)しか読んだことないし。
(define-syntax cons1 (syntax-rules () ((cons1 obj strm) (cons obj (delay strm)))))
(define-syntax cons2 (syntax-rules () ((cons2 obj strm) (delay (cons obj strm)))))
(define (map1 func strm) (if (nil1? strm) nil1 (cons1 (func (car1 strm)) (map1 func (cdr1 strm)))))
(define (map2 func strm) (delay (force (if (nil2? strm) nil2 (cons2 (func (car2 strm)) (map2 func (cdr2 strm)))))))
Shiro: あーっと、Gaucheのsrfi-40実装ではpromise作っちゃいます。 というかsrfi-40のreference implementationがやってることも、 自前でpromiseに相当する機構を作っているだけなので、 「delayを使うよりもメモリが節約できる」ということはないはず。
但し、「R5RS式のdelayとforceだけを使う遅延評価機構でeven styleの streamを書くと、たとえ内部の手続きを末尾再帰で書いていても、 大元のdelayが計算結果を握りっぱなしになってメモリが食いつぶされる」 という問題がありました。それがsrfi-40の議論で明らかになって、 srfi-45においてlazyという機構を導入することで解決が図られました。 現在のsrfi-40のreference implementationは自前でlazyに相当する 機構を実装しています。
Gauche 0.8.6ではネイティブでsrfi-45のlazyをサポートしていて、 srfi-40はそれを使って実装されます。ので、srfi-45で議論されてる space leakは起こらないはず。でもdelayやlazyはその性質上、呼ばれた 時点の環境をセーブしなくちゃならないんで、クロージャを作るのと 同じくらいのメモリ/時間は喰います。