前回の終わりに、いくつか欲しい機能を書きました。
前半はその機能を用意し、後半に Constraint の編集機能を追加したいと思います。
BulletSharp の Demo プロジェクトに同じ物があるので、ジッと見てパッと用意。
ほとんど同じロジックで書いたので、解説は不要と思います。
右クリックは選択処理に使われているので、Ctrl + 右ドラッグでピックできるようにしました。
正しく動作しているようです。
以前説明しましたが、Kimberlite は ExtRigidBody という、拡張剛体を使っているので、
メンバに初期姿勢を持たせて、停止時に物理シミュレーションを止めて、この情報を元に再配置する。
これで解決できました。
Blender を参考にしてみます。
Shift + S キーで次の Snap メニューが出るようです。
選択をカーソルに、選択をグリッドに、カーソルを選択に、カーソルを初期位置に
カーソルをグリッドに、カーソルをアクティブに…なるほどね。
で、細かい修正は座標指定できるという仕組みです。
現在、コンテキストメニューは Add メニュー一つだけです。
同じようなことを実現するためには、押したボタンにより
コンテキストメニューを切り替える必要があります。
WPF ではこれを Resources で実現します。
まず Add メニューと、 Snap メニューを Resource として登録します。
切り替え時に指定するキーが必要なので、それぞれに適当なキーを決めておきます。
で、コードにてウィンドウのロード時にこれらメニューのリソースを取得します。
あとは使いたいタイミングでアクセスするだけ。
この書き方なら右クリックでメニューを表示させずに済みます。
(右クリック時に隠すという、前回の急場しのぎのコードは不要となったので消しました。)
Shift + S を押した結果を次に示します。
Shift + A では Add メニューが表示されることを確認しました。
メニューの項目に対するロジックは適当に組むとして
続いて、座標指定で 3Dカーソルを移動できるようにします。
新しくプロパティグリッドに Kimberlite タブを追加し
このタブでツールのプロパティを編集できるようにしました。
動作を確認、今後も何かツール独自のプロパティを編集したい時はこのタブに追加する予定です。
では、後は簡単なロジックを組む作業です。
<作業>
動作確認できました。
ところで C# ってクラスのディープコピーは特殊なコードを書かない限り起こらないはずでは?
そう習ったのだけど…
クラスを別の変数に格納して、それを弄った場合と、大元のクラスを弄った場合とで動作が変わった。
まぁ、自分の理解が足りていないということでごめんなさい。
新しいデータフォーマットを定義します。
基本 ColladaBinary をパックして、追加データを加えるというものです。
<考え中>
はい、定義しました。
(頭痛くなるまで考えたけど、きっと不備があり、後で手直しすることになるだろう。)
紫色の剛体情報がバカみたいに多いです。
(そんなところまで調整するのかよ…と思いながら策定しました。)
ColladaBinary はパックせず、パスで参照するようにしています。
例によってツッコミどころが満載なので詳細は載せません。
さて、ではデータの書き込みクラスと読み込みクラスを作ります。
<作業>
ひとまず、ヘッダーブロックとフッターブロックを読み書きできるようにしました。
動作を確認
これから、残りのブロックの処理をひたすら書き続けます。
(単なる作業なので、正直つらいです。)
<丸二日間コーディング>
なんとか組みあがりました。
案の定フォーマットは大幅に変わりました(笑)
さっそく動作確認をしてみましょう。
まずはおさらいです。
次のようにテクスチャ付きキューブを縦一直線に並べて
Blender より Collada ファイルを出力します。
コチラを Kimberlite で読み込むと次のとおり。
正しく読み込み、表示ができているようです。
適当に Point2Point コンストレイントを配置します。
↓の図はピック機能で遊んでいる瞬間です。
触っていてアンドゥ・リドゥ機能が無いと大変不便であることに気付きました。
(この確認が終わったら、機能追加する予定です。)
ピックした瞬間を一時停止した状態で Kimberlite ファイル(.kbl)を保存し、情報をクリアします。
で、先程保存した .kbl ファイルを読み込みます。
ドキドキしますね…
読み込み成功を確認!
よっしゃー!やった!
2日間の苦労(苦痛と言ってもいい)が報われた瞬間でした。
参考に実際に使った RigidBody の作成コードを次に示します。
ほぼすべてのデータを書き出しているので、一時停止したその瞬間から再生できる状態でロードが完了します。
コメントアウトしている情報は書きだしたはいいけど、読み取り専用で外から設定できない情報です。
コンストレイントについて
情報を復元する際にちょっと手を入れてあげる必要がありました。
オブジェクトとオブジェクトを結びつけるのがコンストレイントなので
復元したオブジェクトのどれとどれを?と判別方法を確立する必要があります。
自分は UserObject プロパティにユニークな名前を入れて、この名前を使って判別するようにしました。
ユニークな名前は適当に決めて良いので、16文字のランダムな文字列を作成する機構を用意して対応しました。
セーブとロードで余分に情報が必要になったという話です。
さて、対応中にまた一つ仕事が増えてしまいました。
できる操作が少ない今のうちにコマンドパターンを入れて、アンドゥ・リドゥを片づけましょう。
Command パターンですか…しばらく前に触れましたね。
045話から約9ヶ月…やっと C# で同じ所まで来ましたね。
(まだスキンメッシュアニメーションや法線マップなどの機能は入れていないけどね…)
WPF で ICommand と書くとコマンド、コマンドバインディングが連想されます。
例えば次のコードは Kimberlite で使っている XAML コードです。
安直に ICommand とは、作らないほうが良さそう、後々自分で混乱してしまいそうです。
ひとまずコマンドのインタフェースと履歴管理クラスを作成しました。
コンストレイントの追加処理をコマンドクラスとして用意し、テストしてみます。
<試験>
アンドゥができるので、間違えて追加したコンストレイントを削除してやり直しができます。
ストレスなく、すべての積み木を数珠つなぎできました。
ということで Ctrl + Z, Ctrl + Y でコンストレイントの追加がアンドゥ・リドゥできることを確認しました。
厳密には選択状態もアンドゥ・リドゥできないとコンストレイントのリドゥが正確に再現できません。
コマンドパターンを組み込んだので、このあたりは気がついたら修正するといった感じで OK です。
作業負荷は大きくありません。
<追記>
選択処理もアンドゥ・リドゥ出来るようにしました。
ついでに選択の中でもアクティブなオブジェクトをオレンジ色で表示するよう修正しました。
では 本題 のコンストレイントの編集機能を実装したいと思います。
追加した後の調整と言った方が表現として合っていると思います。
例えば Point2PointConstraint の場合は次のパラメータが編集可能です。
PivotInA
PivotInB
Damping
ImpulseClamp
Tau
どのコンストレイントを編集するのか決めるため
まずはコンストレイントの選択機能を付けようと思います。
さて、Kimberlite は現在 RididBody しか書き出し、読み込みを行えません。
これはそもそも World に登録できるオブジェクトが全て RigidBody だったからですが…
衝突判定だけが目的のオブジェクトのために重力などの剛体シミュレーションに時間を割くのも
良くないので、衝突オブジェクトも書き出し、読み込みできるようにします。
<実装>
これまでの剛体の情報設定、取得の部分に手を加えて剛体と衝突オブジェクトで分岐することで
特に難しいことなく実装できました。
また、レイキャストで選択できるが、他の剛体に影響を及ぼさないセンサオブジェクトも必要です。
レイキャストやヒット判定に含まれるが、衝突対象から外すといった設定は World への登録時に
グループとマスクのビットフラグを指定することで実現できます。(038話 衝突グループの設定(フィルタリング)より)
大きいキューブが剛体、小さいオブジェクトが衝突オブジェクトです。
センサオブジェクトとして、他の剛体に影響を及ぼさないことも確認しました。
参考に、フラグ設定は次のコードで行います。
登録後、このフラグ情報を取り出す方法を探していますが、不明です。
まぁコチラから設定する情報なので困りはしないと思いますが、実装時には注意が必要です。
ColladaBinary の物理ベース情報にも、剛体か衝突オブジェクトかの情報を付加します。
センサオブジェクトを作れるようになるので、コンストレイントを描画している所に
このセンサオブジェクトを配置し、選択操作できるようにするという魂胆です。
<実装>
うまくいったようです。
実際はセンサオブジェクトを非表示にしますが、デバッグ用に表示しています。
選択したオブジェクトからコンストレイントを取得出来るようにして
後はプロパティグリッドに、選択したコンストレイントのプロパティを表示出来るようにすれば
今回の目標はクリアです。
<実装>
気になって仕方がないので、先に片付けます。 やっぱり次のコンストレイントに対して
センサオブジェクトを配置するなら…
こう↑ですね。
はい、ということでプロパティグリッドの更新処理に手を加えて
選択したコンストレイントのプロパティを表示するようにしました。
(選択カラーは青白く光る感じにしています。)
デバッグ用に Test 用 int プロパティだけ表示しています。
こちらを Constraint 用のプロパティに書き換えます。
<作業>
はい、書き換えました。
動作を確認、追加した Point2Point コンストレイントのプロパティを
編集できることを確認しました。
また編集した結果が即時反映されることも確認できました。
あとは、これを保存して再度読み込む事ができることを確認するだけです。
確認できました。
と、いうことで今回のお話のノルマ達成〜。
前回の自分のクレームに対応するのがそこそこしんどかったです。
破壊衝突閾値…というものを低めに設定してみました。
例えば 30[N] ニュートンとか↓
強めに引っ張るとプッツリと切れてしまいました。
ほか Setting プロパティの Damping を操作したのですが
編集後にまた 1 に戻ってしまうことを確認。
どうも編集できないようです。
Point2Point の Damping くらいは操作したかったのですが…残念。
Erp, StopErp, Cfm, StopCfm というパラメータも編集できるようにしましたが
これらが動作にどう影響を及ぼすかは、ドキュメントが無いことにはわかりません。
公式の解説ページを参照しましたが
資料なし、元々物理には詳しくないので、必要に迫られたら詳しく調べる予定。
>物理演算ライブラリ群のbulletの日本語解説サイトとして機能するのはおそらくこのサイトのみでは?今後も期待です。
恐縮です。メッセージありがとうございます。
Bullet について不明な点を調べると過去に自分が書いたページだった、なんてことが多々あります。
>助かります。
お役に立ててうれしいです。
>何処にコードがある?
PropertyTools for WPF のサンプルプロジェクトは次のリンク先の「Download」ボタンから入手できます。
PropertyTools for WPF - SOURCE CODE -
わかりづらいかもしれないので、参考画像も示しておきます。
2012/10/27 初記。