プログラミング一覧

スキンメッシュ行列

リフレクション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の部分も

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

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


DirectX10 (D3D10)

Tutorial02解説

今回は、DX10での三角形(ポリゴン)描画までのサンプルを読み解いてみました。
前回までの初期化にプラスして、頂点データの作成、シェーダーの作成と要素が詰まってます。

シェーダーの作成は以下のようになってました。若干自分なりのコメントをつけてみました。

間違っている場合もあると思いますが、気づいた人はコメントでご指摘ください。(^^;

[code lang=”cpp”]
// Create the effect
// このフラグは新しいHLSLのセマンティクスを有効にするため?
// 従来のHLSLコードを通過させないためのフラグである模様
DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif
hr = D3DX10CreateEffectFromFile( L"Tutorial02.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0,
g_pd3dDevice, NULL, NULL, &g_pEffect, NULL, NULL );
[/code]

どうやらDX9時代と変更がない感じです。プロファイルが fx_4_0になったくらいでしょうか。
シェーダーのフラグでDX10用のHLSLを要求しているように見えます。

fxファイルを覗くとわかるのですが、technique10や今までに見慣れないセマンティクスが使われています。
これらを要求するためのフラグみたいです。

[code lang=”cpp”]
// Define the input layout
D3D10_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = sizeof(layout)/sizeof(layout[0]);
// Create the input layout
D3D10_PASS_DESC PassDesc;
g_pTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
// 上記で設定したinput elementと、シェーダーのバイトコードからinputレイアウトの作成.
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &g_pVertexLayout );
if( FAILED( hr ) ) return hr;
// Set the input layout
g_pd3dDevice->IASetInputLayout( g_pVertexLayout );
// Create vertex buffer
SimpleVertex vertices[] =
{
D3DXVECTOR3( 0.0f, 0.8f, 0.5f ),
D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),
D3DXVECTOR3( -0.5f, -0.5f, 0.5f ),
};
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 3;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
if( FAILED( hr ) ) return hr;
// Set vertex buffer
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
// Set primitive topology
g_pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
[/code]

シェーダーのバイトコードと、頂点宣言二つからinputlayoutを作成します。
そしてこのレイアウトはIA(InputAssembler)にセット。
この次に、ようやく頂点バッファの作成に取りかかります。

DX9時代とかなり変更になっているのがこの部分。
BufferDescriptionとSubresourceDescriptionの二つから頂点バッファを作成しています。

各DESCに値をセットして、初期化時にデータを転送してしまうっぽいです。

このD3D10_USAGE_DEFAULTフラグでは、GPUからのアクセスはできて、CPUからのアクセスは禁止という意味だそうです。
DX10からはすべてのバッファがMap(従来のLock)ができるわけではなくなりました。

つまり上記の例ではMapすら禁止ということで、初期化時にシステムメモリ上のデータを設定することで、初回1度だけデータ転送しているということなんでしょう。

SubResourceとはなんぞや?

よくわからなかったので調べてみました。

上記でもSubResourceDescription を設定してます。
このdescではバッファの初期データを与えるためにセットしています。

SubresourceDescriptionは実際のリソースデータを示し、データのサイズとレイアウトに関する情報を保持します。

どこかにも記述があったのですが、Subresourceっていわゆる実データなんでしょうか。
Surfaceに相当とか書いてあった気が…。