「 プログラミング 」一覧

スキニングメッシュ


ようやくtiny.xを自前フォーマットに変換して描画まで確認。
苦労したけど、サンプルのSkinnedMeshのデータと比較できたのでまだ楽だった。

スキニングの一番の問題点は、行列のデータを見ても全く正しいことがわからないこと。オフセット行列も正しいか謎だし。簡単なサンプル作るにしても、スキニングメッシュを作るには、一般的にはモデラーが必要になるし。

今回の一番のポイントは、DrawSubsetらから抜け出せたこと。

drawSubsetの中身は結局は描画バッチを実行しているだけに過ぎない。
内部では、DrawPrimitive呼び出しを実行しているだけだろうし、その部分を自分で何とかやろうとしてみた。

ConvertToIndexedBlendedMesh を実行すると、属性グループ、ボーンコンビネーションバッファが取得できるのでこれから何とでもなる。

pBoneComb[ AttibId ]で、1回のDrawPrimitive呼び出しに必要な頂点数、インデックスなどは入っているし、

必要とするボーン(フレーム)へのIDも取得できる

pBoneComb[ AttribId ].BoneId[ n ]

ここで、必要とするボーン数はパレットの数としてメッシュコンテナが取得しているので、そのループで回せば必要とするボーンIdが取れる。

このボーンIdから、さらにGetBoneName( BoneId )すれば対象とするフレームの名前が取れる。

注意点としては、パレット数(nNumPaletteEntries)はシェーダーのマトリックスパレット数で制限される。で、複数回に分けられた後、最後の描画グループでは、パレットが全て埋まるわけではない。

tinyでやったところ、13という中途半端なものになった。
このとき、BoneIdが UINT_MAXになるのでその場合は取得しないなどの条件が必要となる。

この辺はSkinnedMeshのサンプルで実装されていて、あぁ、なるほどと思ったポイント。


スキンメッシュ行列


リフレクションlibにバグが潜んでいた

抽象クラスはNG. templateのインスタンス化後、メンバ取得のために抽象クラスのインスタンス作ろうとするのでNG
ユーザーのオブジェクト型をさらに派生したクラスでのエラー

どうやら、class Aを継承したclass B, class Cが居て、それぞれの初期化と、A自身の初期化で、おかしな状況になっていた。こちらは修正済み。

しかし、最初の項目はシステム上どうしても無理だ…。
抽象クラスのメンバのシリアライズは派生先で再定義してもらうしかないのか…。システム的に破綻っぽい orz

スキンメッシュ

とある場所で、ボーンの部分で、ローカルのワールドの逆行列という記述をみた。
さっぱりなので、色々と調べてみた結果このような表記はXSIの部分で使われているようだ。この行列Inverted_matBasePoseとかOffset行列とかいろいろな呼び方をされている模様。

モデルが初期ポーズをとっている状態でボーン空間をきちんと合わせるのに必要な行列ということらしい。

各ノードのワールドマトリックスを作るには、


ここで逆変換を作用させて、ボーンのローカル空間へ. 骨のポーズ状態が原点としているらしい…

“日々精進時々堕落”というWebが参考になりました。
各状態が算出された後の実装方法は、DirectXのSkinnedMeshサンプルで何とかなるんですが、それに必要なデータの情報ってなかなかないですよね。

 


(no title)


[プログラミング] スキンメッシュ注意点

スキニングメッシュを読み込むときには、

  • D3DXMESHCONTAINER
  • D3DXFRAME
  • ID3DXAllocateHierarchy

らを派生し、ロード&生成コードを自前でかかなくてはいけない。

ここで、派生の決まり事である、デストラクタはvirtualを付けよ。
という点を無意識のうちに書いていて、ミスを犯した。

ルートとなるD3DXLoadMeshHierarchyFromXでの結果格納のpFrameで、
ウォッチで値を見てみると数バイトずれて結果が表示されていた。
そこで疑いを持った。

  • 呼び出し規約でも間違えた?
  • virtualが悪い?

のどちらかがまず怪しい感じがしたので見てみた結果比較的短時間でわかった。

あと気になったのは、ボーンのオフセット行列。
メッシュコンテナに入っていて描画時はそこまで違和感を感じないのだが、
なぜボーン(Frame)の個々の要素として持たないんだろうと。

モデラーから出力されたデータを想定すると、各ボーン毎にオフセット行列があっても良い気がする。


スレッドプログラミング


Java互換スレッド操作ができるライブラリ作成中。

あちらこちらでバグにはまり、時間だけ浪費している感じですがようやく動くようになってきました。

待ち行列とsynchronizedクラスのペアで操作
この部分で、1日ほぼ費やしました。

[code lang=”cpp”]synchronized lock( waitingQueue->getCriticalSection() );
{ // データがないので待つ
waitingQueue->wait();
}[/code]

という操作している部分で、2重ロックしていたという…。

結論、待ち行列管理する部分でのwait時にはロック保持しないことに。

そして、動作チェックということで、マルチスレッドのデザインパターンサンプルを作成してみたり。


リモートデバッグ


DirectXのプログラムを作っているわけですが、開発機が既にVistaのため、デバイスロストを発生させることが出来ないので、WindowsXPのマシンを用意してリモートデバッグを試みてみたのですが、なにやらうまくいかず…。実行体を全く同じパスにおいて実行してみるも、拒否されたとか言われてしまう。

WinXP SP2での変更のため、MSDNヘルプの通りにやっても動かないという話もあるし。

とりあえず試したのは、

ファイアウォールの無効化D
COMのリモートによる起動を許可

あと、XPHOMEではローカルセキュリティポリシーが存在しないため、そもそもリモートデバッグのクライアントを入れることが不可能ぽいです

 

DirectX環境依存

SetRenderStateのカリング指定で、無効な値を入れてしまったときの挙動が環境によって異なっていた。GeForce系は無事に動いていたので発見が遅れました。

ちゃんと設定しないとねーということで。

他にも、デバイスロスト時のリソース復帰について環境依存が。
D3DPOOL_MANAGEDであってもテクスチャが正常に復帰されないバグに見舞われました。

環境は 945GMのチップセット統合。他には915GMでも起こると推測されます。

結局のところ、Managedは当てには出来ないのかもということになり、自前でリストアコードを書いた方がきっと無難だろうなと思います。

 


d3dx_*.dll 非依存


ある人に聞いてみたらやはり可能とのこと。

事前コンパイル済みのシェーダーを使うことで何とかなりました。この場合の問題点は、名前ベースでシェーダー定数にアクセスできなくなること。
やるとすれば、VertexShaderConstantFなどを使い、レジスタ番号指定してデータセットになります。
シェーダーのバイトコードを解析して、定数レジスタと名前などマッピング取れるかなと思いましたが厳しいかも。ゆるくやるには、d3dx.dllは切れない感じですね。

考えてみた方法1

事前コンパイルしたバイトコード+ConstantTableの情報を、オリジナル形式でファイルに書き出したらどうだろう。
ConstantTableから名前→レジスタ番号が取れれば、それも事前にファイルに記録しておくことで何とかなるかも。

 


久々に template C++


変数が配列かのテスト

templateと関数のオーバーロードなどを駆使して作ってみた。一応うまく動いているものの何か気になる。まずい点とか誰か指摘してくれないかなぁ…

使うときには、 int array[ 10 ];なるものを、 testArray( array )とやって、trueが返ってくれば 配列である。テンプレート使っているものの、関数呼び出しも使っているので、inline指定しているといってもコンパイル時に実行コードが決定されると言えないかもしれない。

最適化がうまくかかってくれれば、コンパイル時にチェック完了で余計なコード消えそうな気もするが…


ジェネリックプログラミングの練習2


ジェネリックプログラミング

今日は以前から気になった部分をやってみました。テンプレートで型1から型2への変換が可能かどうかをチェックするという代物です。これにより変換が可能だったら〜というプログラミングが可能になります。

個人的にはこれから多用しそうな予感。

[code lang=”cpp”]
template< class T1, class T2>
struct can_convert {
static yes_type check(T2);
static no_type check(…);
static T1 makeTo();
enum {
  value = (sizeof(check(makeTo())) == sizeof(yes_type))
};
};

class Base { };
class Derived : public Base { };
class Test { };

int main() {
bool canConv1 = can_convert<Derived*, Base*>::value; // true
bool canConv2 = can_convert<Test*, Base*>::value; // false
bool canConv3 = can_convert<int, float>::value; // true (with warning

return 0;
}
[/code]


いざ魔界へ


Boost魔界へ

以前のリフレクションシステムは、会社でのソースを見た後だったので、
恐ろしく構造が似ていました。いやー、見てもらったらパクリx2 言われてしまった。
実装はtemplateメインにしたのでちょっとは違うのですが。
使うユーザー側にとっては定義の仕方一緒だし…。

そんな中最近のBoostにはserializationが入ったらしいです。
これを使えばシリアライズできるじゃん!ということです。
ただシリアライズはできるもののフィールドに対するメタ情報を扱うわけではないです。

そういう背景もあって自分でなんとか作ってやろうじゃないかと勉強中です。

そのためにはBoostの該当コード理解しないとと思ってますが、魔界です…

正直何やってるかわからない。template地獄。実行させながら追ってみるもののやはりさっぱり。

出直してきます・・・・・ orz

とりあえず自分に足りない要素として、

  • メタプログラミングに対する理解が不十分
  • templateによるジェネリックプログラミングの理解

とくに後者は多段のtemplateが使えるようにならなきゃ話にならないですね。


DirectX10 (D3D10)


Tutorial04

今回はTutorial04を見てました。このサンプルは立方体(キューブ)の表示サンプルでした。
前回のTutorial02の部分に比べて増えたところと言えば、IndexBufferを作っているところです。
ほかには、シェーダーで透視変換させているところ。大きく変わった部分はないです。

IndexBufferもVertexBufferと同じくBufferDescriptionとSubresourceDescriptionの2つを設定して、デバイスのメソッドで作成するだけです。

変更になってる部分はデータをどう使うか指示するところ、
「D3D10_BIND_INDEX_BUFFER」「D3D10_BIND_VERTEX_BUFFER」の違いくらいです

描画する前には以下のように

各バッファをデバイスに設定し、プリミティブタイプを指定しておきます。この辺が若干以前と変わっています。
前はDrawPrimitive系でプリミティブタイプを指定していました。

また、インデックス用のフォーマット指定(D3DFMT_INDEX16/32)がありましたが、
どうやら今回からは統一されたようです。

気になったのはこのサンプル、インデックスに32ビット指定してます。
従来は16ビット(WORD型)を基本的に使っていたのに、このように変更となるとデバイスの最適化もこれからは32ビットインデックスが主流となっていくんですかねぇ…。(※ 現状のデバイス(当時)は16ビットインデックスに最適化されています)

シェーダーに対して、各マトリックスをセットして、パスを回しています。
見てわかるように描画のDrawIndexedの引数が減っています。すっきりしましたね〜。あとDX9世代でBegin/BeginPassで行っていたパスを回す処理が変更になっています。テクニックのDescから値を取得して、回数と使用指示を設定しているようです。

この辺は使う側としてはあまり変更がないといってもいいでしょう。
あまり大きな変更ではないように思います。

気になったのは今までシェーダー定数を書き換えたときには、CommitChangesなどのメソッドを呼んで確定させていた気がしますが、それが消えています。

シェーダーコード

HLSLの中身も若干変わっています。

今までfloat4x4とかやっていた部分がmatrixになっています!

Passの部分も

となっており、ぱっとみてわかる程度の変更です。

ここにジオメトリシェーダーの記述があって、そのうちいじってみたいなとわくわくです。