2012年6月3日日曜日

Maya CardStickの解説2

さて、前回「Maya CardStickの解説1」の続きで、

Particleがオブジェクトに衝突して、 衝突したオブジェクトにくっつく件です。


 大雑把な流れとしては、
if( コリジョンした時 )
{
 // cube1のInverse Matrixを取得
 float $invm[] =`getAttr cube1.worldInverseMatrix`;

 // collisionした時のParticleの位置取得
 vector $p = position;

 // $pにmatrix $invmを掛け算
 vector $newP = multMat( $p, $invm );

 // この時の値を保存
 collisionPosPP = $newP;
}
else if( コリジョンした以降のフレームでは )
{
 // cube1のMatrixを取得
 float $m[] =`getAttr cube1.worldMatrix`;

 // 保存した値を取り出し
 vector $p = collisionPosPP;

 // $pにmatrix $mを掛け算
 vector $newP = multMat( $p, $m );

 // 値を入れる
 position = $newP;
}
と言う感じでしょうか。 めんどくさくなって来たので、とりあえずコードを載せます。
以下はRuntime after dynamicsのExpressionです。
// コリジョンした場合に0から始まる数字が入ります。
// コリジョンしていない時は-1が入ります。
int $gid = particleShape1.collisionGeometryIndex;

// コリジョンした時
if( $gid!=-1 )
{
 // get collision shape
 string $geo = getCollisionShape($gid);

 // get inverse Matrix of collision shape
 float $m[] = `getAttr ($geo+".worldInverseMatrix")`;

 // multiply $p * $m
 vector $p = particleShape1.position;
 $p = multMat( $p, $m );

 // set attribute
 particleShape1.collideGeoId = $gid;
 particleShape1.collisionPosPP = $p;
 particleShape1.traceDepthPP = 0;

}
// コリジョンした以降のフレーム
else if( particleShape1.collideGeoId!=-1 )
{
 // get collision shape
 $gid = particleShape1.collideGeoId;
 $geo = getCollisionShape($gid);

 // get Matrix of collision shape
 float $m[] = `getAttr ($geo+".worldMatrix")`;


 // multiply $p * $m
 vector $p = particleShape1.collisionPosPP;
 $p = multMat( $p, $m );

 particleShape1.position = $p;

}
float型のPar Particle AttributecollideGeoId
vector型のPar Particle AttributecollisionPosPPを追加しておいてください。

まず、 最初のif文、コリジョンしたかどうかは
collisionGeometryIndex
と言うAttributeを見ると、-1ではない値が入るので分かります。

次に9行目
getCollisionShape();
と言う関数でコリジョンしたShape名を取得するようにしています。
これは、コリジョンしたオブジェクトのshape名を取得する関数で、 以下を作成しました。
この関数定義も Runtime after Expressionの最初の方にでも書いておきましょう。
proc string getCollisionShape( int $gid )
{
 string $gshape;
 string $aGeoConnect[] = `listConnections particleShape1.collisionGeometry`;
 string $aGeo[] = `listConnections -s 1 -d 0 -p 1 ($aGeoConnect[$gid]+".localGeometry")`;
 string $aAttr[] = stringToStringArray($aGeo[0], ".");
 $gshape = $aAttr[0];

 return $gshape;
} 
何をやっているのかと言うと、
Particleにコリジョンするオブジェクトは「Make Collide」でコリジョン設定していると思いますが、
その際、 オブジェクトのShapeは、Particleのアトリビュート「collisionGeometry」にコネクションされます。
そして、 「collisionGeometryIndex」で得られる番号が 「collisionGeometry」に接続されている配列番号に対応しており、
実際には、「particle <- geoConnector <- mesh 」と言うノード構成で接続されているので、
それをたどってShape名を取得するようにしています。

次に、16行目の以下の関数ですが、
$p = multMat( $p, $m );
これは、 引数vector型$pにmatrix配列$mを掛け算してvector値を返す関数で、

melコマンドとして以下があるのですが、
やけに処理が遅い上に、結果が変でした・・・
float[] pointMatrixMult ( float $point[], float $matrix[] )
なので、melで組んでます。
この関数も Runtime after Expressionの最初にでも書いておきましょう。


proc vector multMat( vector $p, float $mat[] )
{
 float $fp[4] = {$p.x,$p.y,$p.z,1};
 float $result[4];
 for( $i=0; $i<4; $i++ ){
  $result[$i] = 0.0;
  for( $j=0; $j<4; $j++ ){
     $result[$i] += $fp[$j] * $mat[$i+$j*4];
  }
 }
 return <<$result[0],$result[1],$result[2]>>;
}
そんで、
collideGeoIdにcollisionGeometryIndex, collisionPosPPにinverseしたpositionを保存し、
21行目で
traceDepthPP = 0;

これは、コリジョン精度になりますが、0にする事でそれ以降コリジョンしなくなります。
で、これをやっておかないと、「collisionGeometryIndex」が常に-1以外になってしまい、
いつでもコリジョンしている状態になるので、これをやります。

コリジョン以降の処理については、
なんとなく分かるかと思うので説明は省略します。


最後に、
コリジョンオブジェクトについている、「geoConnector」ノードの
「Friction」は1にしておいてください。

1以下だと、Particleがコリジョン時に一瞬滑ります。
何でしょうかねぇ・・・
コリジョン時にExpressionの計算が聞いてない感じで・・・

では次回は変形しているオブジェクトへの対応についてか、
Rolling Ballについてか、その辺を書こうかなと。