過去の独りごち/独りごとはこちら
過去のJavaアプレットは
こちら

 

9/3 解説 新SXLib

 随分前に作ってほったらかしにしていたといいますか、ちまちま使ってはいたものの、ろくに手を入れていないDG-Carad専用3Dツールキット、SXLibについて今回は取り上げてみようかと。

 なんで突然そんなことをするかといいますと、自分自身作ったきり忘れてしまった、とか、内部構造を文書に書き出してみる事でまだまだ作り足したい部分が見えてくるかもしれない、とか、そういう気持ちがあるのです。

SXLibの構成

 まず、SXLibを構成するクラスについてざっと見てみましょう。SXLibを構成するクラスは、当たり前ですがそれぞれが3Dシーンの構築に重要なウェイトを占めています。

 

TSXMesh  メッシュの管理をするクラス。メッシュの読み込み・保存が可能(独自形式)
 メッシュとテクスチャは独立したデータであり、メッシュは頂点データと頂点インデクスの組みとして定義される。
 最大65535頂点から構成される。(頂点インデクスは231個まで可能)
 描画時は一塊のTRIANGLELISTとして描画。
 上記制限のためパフォーマンスは最高とは言えないが、VCacheを意識した最適化や同じ要素を持つ複数の頂点をまとめるメソッドなども用意されている。
TSXFrame  ローカル座標系の管理をするクラス。
 最大1つのメッシュと、それに対応したテクスチャ(ステージ0〜7まで)・マテリアル・ラップなどのための各種レンダリングステートを関連付けられる。
 フレームに格納されたメッシュの描画の際に、フレームの座標系が適用される事になる。
TSXMeshList  メッシュ用のTListみたいなモノ。
 複数のメッシュをまとめて保存・読み込みを行うためのメソッドが用意されている。
TSXFrameList  同じく、フレーム用のTListみたいなモノ。
TSXLightGroup  光源群の管理
TSXScene  上記のオブジェクトに格納された情報からシーンを描画するクラス。
 他に、ビルボードやスプライトの描画、ビューポートの管理なども行う。
TSXTextWriter  文字列描画用ユーティリティクラス

 このうち、TSXFrameオブジェクトとTSXSceneオブジェクトの操作がSXLibの利用のうちほとんどを占めるのではないかと思います。

 よく誤解されるのですが、SXLibは3Dに特化していて2Dにはあまり向かないという印象を抱かれているようですが、むしろSXLibは2D描画と3D描画の調和に特化した3Dエンジンという事が出来ますし、3D描画を一切行わずにPushSpriteメソッドだけ用いて2D描画だけ行わせるという事も充分可能です。

 確かに、旧Primitivesのように2D描画だけに特化したエンジンにはパフォーマンスや学習しやすさの面で明らかに劣ると思うのですが、そうしたエンジンを利用すると、作ってるうちに3D描画も混ぜたくなっちゃったという時に困るのは明白です。

 さて、大体どんなデータを用いて2D/3Dシーンを作っていくのかという事について押さえたところで、一体どうやってTSXSceneは描画を行うのか、その流れについて見て行きます。これを知ることで、SXLibが内部で何をやっているのかが随分ハッキリした物になると僕は思っています。

TSXScene.Renderメソッド

 TSXScene.Render(rootFrame : TSXFrame);

 というのが呼び出し形式になっていて、描画をする上での最上位のフレーム = ワールド座標系になるフレームが渡されますが、描画ルーチンそのものの説明に入る前に、レンダリングキューについて説明しておきます。レンダリングキュー(以下、単にキューと呼びます)とは、メッシュやビルボード、スプライトといった、とにかく「画面に描く必要のある何か」についての情報を格納しておくためのものです。

 なぜキューイングをする必要があるかというと、半透明体を書く際のZソートのためです。TSXSceneでは半透明体のようにZソートの必要がある物体のためのキュー(Sortキュー)と、不透明体のようにZソートの必要の無い物体のためのキュー(普通のキュー)、そして、スコア表示やHUDなどの描画のために無条件で最前面に書かれるようなスプライト用のキュー(Topmostキュー)を用意しています。

 まず、スプライトとビルボードに関しては、Scene.PushSprite、Scene.PushBillboardによってSceneオブジェクトに渡されるたびに、キューに放り込まれます。Sortキューに放り込むか普通のキューに放り込むかはそれぞれのメソッドのblendmode引数によって決定されます。

 メッシュに関しては、メッシュの格納されたTSXFrameへの参照がキューに放り込まれる形になります。この理由は後で説明します。

 ちなみに、半透明体のソートをするのは重くなるし、適当でいいや、という場合は半透明体同士のソートを行わないようにもできます。TSXscene.SortAlphaプロパティをFalseに設定すればOK。デフォルトではTrueに設定されています。

 以上を踏まえて、レンダリング手順を見て行きましょう。

  1. カメラの情報から、ビュー行列の設定
    TSXScene.CameraFrame.WorldMatrixの逆行列(TSXFrame.ViewMatrixと等価)を、ビュー行列にします。
    投影行列とビューポートはRenderメソッド以前の段階ですでに設定されているはずなので、ビュー行列・投影行列とも作成できるのです。
  2. rootFrameと、その子孫にあたるフレームについて、可視判定をパスした物をキューに入れます。
    その具体的な手順はこちらをどうぞ
  3. キューに入っているスプライト・ビルボード・フレーム(まとめて「物体」と呼びます)を以下の流れで描画します。
    ただし、フレームに関しては、OnRenderがイベントが設定されているならそれを呼び、設定されていなければ格納されているメッシュを描画するようにします。
    1. Zバッファへの書き込み/比較をONにして、普通のキューに入っている物体を描画
    2. Sortキューに入っている物体をZ値に応じてソート
    3. Zバッファへの書き込みだけOFFにして、Sortキューに入っている物体を描画
    4. Zバッファへの書き込み/比較の両方ともOFFにして、Topmostキューに入っている物体を描画

 以上で、Renderメソッドの説明は終わりです。文章にしてみると案外あっさりでしょうか?

SXLibが勝手に変更しやがるレンダリングステート等について

 最後に、SXLib側で勝手に操作してしまうレンダリングステートについて述べておきます。これを知っていれば、「あれ、自分のプログラムで設定した内容が反映されてない??」という事が少なくなるかと。

TSXScene.Renderメソッド内

  1. ステージ0のテクスチャ
  2. ステージ0のD3DSAMP_MAGFILTER
  3. ステージ0のD3DSAMP_MINFILTER
  4. ステージ0のD3DTSS_COLORARG1
  5. ステージ0のD3DTSS_COLORARG2
  6. ステージ0のD3DTSS_COLOROP
  7. ステージ0のD3DTSS_ALPHAARG1
  8. ステージ0のD3DTSS_ALPHAARG2
  9. ステージ0のD3DTSS_ALPHAOP
  10. D3DRS_SPECULARENABLE
  11. D3DRS_LIGHTING
  12. D3DRS_WRAP0
  13. D3DRS_FILLMODE
  14. D3DRS_ZENABLE
  15. D3DRS_ZWRITEENABLE
  16. D3DRS_ALPHABLENDENABLE
  17. D3DRS_BLENDOP
  18. D3DRS_SRCBLEND
  19. D3DRS_DESTBLEND
  20. D3DRS_ALPHATESTENABLE
  21. D3DRS_ALPHAFUNC
  22. D3DTS_VIEW
  23. D3DTS_WORLD
  24. D3DDevice.SetMaterialにより、マテリアルの変更

TSXScene.SetProjection内

  1. D3DTS_PROJECTION

TSXScene.SetViewport内

  1. D3DDevice.SetViewportにより、ViewPortの変更

TSXLight

  1. D3DDevice.SetLight, LightEnableによりライト情報の変更

以上ッ

 ふ〜っ。以上でSXLibの内部についての解説を終了します。

 眺めていて思ったことといえば、やはりVS/PSを考慮していなかったり、DirectX9で復活したGetDCを活用していなかったりと、いささか時代遅れの実装になっていることでしょうか。VS/PSを自前のものに設定してしまうと、レンダリングステートのいくつかは完全に無視されますので、折角SXLibで色々とセットしたレンダリングステートは、単なるムダという事になってしまいます。

 ともあれ、これからやらなきゃならないことはまだまだ、ですね〜。なにより作品作り、かな(^^;)

Taku Hayase(SANDMAN)

戻る