グラフィックスAPIとwindow

 GPUPPURは基本的にDirectXOpenGLPhysXAPIさえ使えばいいと思ってたのですが、DirectXとかOpenGLを使うプログラムには

  • windowの作成、破棄
  • 作成したwindowへレンダリングできるようにする
  • windowに送られるメッセージの処理等

が必要となります。
上のやり方はDirectXOpenGLで違っていたり、windowsOpenGLX11OpenGLで違ってたりします。

せっかくGPUPPURが複数のOS、DirectXOpenGLの両方に対応したとしても、
ユーザが環境ごとにwindow関連の環境依存コードを書いていたのでは、とっても面倒です。
GPUPPURをDirectXで実装したとき、OpenGLで実装したとき、Linuxでテストするときで別々の処理を書くのは面倒です。
OpenGLにはGLUTというとても便利なライブラリがあり、GLUTwindowsX11でのwindow関連処理を吸収してくれます。
つまり、GLUTライブラリを使って書いたコードはOSが違っても、そのままコンパイルし直すだけで移植できちゃいます。
DirectXにも似たようなライブラリであるDXUTというのがあります。元々DirectXwindowsでしか使えませんから、
移植できるとかっていうことは問題でないのですが、DXUTではwin32APIをあまり気にせずにGLUT風のcallback関数を使ったプログラミングができます。

 ところがどっこい、DXUTは複数windowには対応してません。以前、バイトで複数windowにDirect3Dレンダリングしないといけないことになり
DXUTを改造(DXUTはソースコードが見れるので)しようとしました。ですが、DXUTを書いた方はどうやら膨大な短期記憶とタイピング速度と中の人しかしらないようなwin32apiやdirectxの詳細な知識を持った方のようで、あんまり深く理解できませんでした。とりあえず、不必要なGUI関連の処理をはずしてみると動かなくなりました。DXUTを弄るのは不正行為ということで、DXUTを少し弄ると動かなくなるようにできているのかもしれません。
今DXUTのソースを見たら、DX10とDX9の両方に対応していているようでした。何箇所かに実行時にどちらが使われているかを調べて処理を分岐させてるようでした。デバイスの設定を変更する関数DXUTChangeDeviceは500行ほどありました。
 というわけで、そのバイトのときは自前でDXUT相当のwindow初期化やらイベント処理やらをやるクラスを作成することにしました。で、今回のGPUPPURはそのとき自作したクラスを改良して使うことにしました。
 そのクラスの主な特徴は

  • windowの作成とそれに関連するDirect3DDeviceの初期化、破棄もやる。
  • 一個のwindowに一つのインスタンスが対応する。
  • ↑のお陰で、WinProcでhWndを調べるだけで、イベントが届いたwindowに対応するオブジェクトがわかり、そのオブジェクトのメンバ関数を呼び出せる。
  • フルスクリーンに対応。自分なりにdevice lostに対する処理もちゃんとやってる。(むしろフルスクリーン化はずっと後に実装してもよかった気が)
  • 作成済みのwindowのhWndを受け取って、そこへレンダリングできる。こういう感じの機能を使って、C++で作ったDirect3DのDLLでC#で書いたwindowにレンダリングとかやってたな。
  • キー入力とかマウス入力を知りたいときはWndProcの中身を自分で書いてくれ。

DXUTと比べると機能は少なく、ソースコードの量も少なめです。(cppファイルの行数はDXUTChangeDeviceの実装より100行程長いですが)

これで自前のDXUT実装したから、後はGLUTとインターフェースをあわせて上手く統合すればOK。DXGLUTの完成かと思ってたら
GLUTC言語用に書かれてます。openGLUTやfreeglutもC言語で書かれてます。glutみたいなやつのc++版がありましたが、glutの関数がそのままクラスのstatic関数になってるだけでした。
そんなこんなで最初freeglutを使って、freeglutの機能(documentに書いてなくて、ソースコードを見たときに発見した関数を含む)をラップしたC++のクラスを作成したのですが、フルスクリーン化がうまくいかなかったので、結局openglutを改造して使うことにしました。(どこらへん改造したか忘れた)
自前glutではイベント起きたときに呼び出されるコールバック関数をboost::functionとしてwindow毎に設定できます。directXのときと同様に一つのwindowに一つのオブジェクトが対応します。また、コールバック関数にオブジェクトへの参照が渡されるので、継承して独自の機能を持たせてオブジェクトのメンバ関数を呼んだりできるはずです。(これはまだテストしてませんでした。)
さらにGLUT同様にコマンドラインの引数を読んで、ウィンドウのサイズや位置等を指定できます。さらにmain関数から始まるときでも、WinMain関数から始まるときでも使えます。(その代わり初期化関数の呼び出し方がちょっと違いますが)
ところがどっこい、windows以外の環境でまだ、テストしてなー。

この自前glutを実装したときに気付いたのですが、windowを作成する関数はwindowのタイトルを文字列として引数をとります。
windowsの場合、文字列はTCHARにしておけば、マルチバイトもunicodeソースコードコンパイルし直せば対応できるようです。Linux等ではUTF-8を使えばcharのままでいいようです。さらに、リンクしているopenglutはマルチバイト用にコンパイルされ、文字列は全部char型。

文字のエンコードにどう対応するかという問題にぶちあたりました。もう世の中すべてUTF-8でいいような気がするので、libutf-8を使って全部utf-8化、必要なところは変換するというのでやるか、charの代わりにGCHARを使い状況にあわせてcharにしたりwcharにするか。tcharをそのまま使うと移植性が・・・・

眠たいのを我慢して書いたのでたぶんおかしいこととか書いてあるかもしれません。ご了承下さい。