btMotionStateメモ

PMDの剛体情報とbulletのMotionStateの関連性がだいたい理解できたのでその辺りについてのメモ。

フォーマットはこちらを参照
http://blog.goo.ne.jp/torisu_tetosuki/e/1e25fc196f2d7a7798f5cea87a942943

また、ソースはARTK_MMDを参考にしています。

btMotionStateの2つの仮想関数の役割について

MotionState::setWorldTransform

毎ステップの物理演算の結果による剛体の重心位置が通知される。
不要な場合は、btRigidBody::setActivationState( DISABLE_DEACTIVATION )で無効にできる。

MotionState::getWorldTransform

剛体位置を更新するときに呼ばれる。
デフォルトでは剛体の初期位置を指定するときに一度だけ呼ばれる。
btRigidBody::setCollisionFlagsにbtCollisionObject::CF_KINEMATIC_OBJECTが指定されると毎ステップ呼ばれるようになる。

btDefaultMotionState

http://www.bulletphysics.com/Bullet/BulletFull/btDefaultMotionState_8h_source.html
剛体の初期位置指定と毎ステップで剛体位置をメンバに保存するだけ。

PMDの3種類の剛体について

種類ごとに色分けした剛体

0:Bone追従(赤)
1:物理演算(緑)
2:物理演算(Bone位置合せ)(黄)
3:関連ボーン無し(腰のあたりの暗い黄)

0:Bone追従

ボーンと同じ動きをする剛体。ボーンの動きをそのまま剛体に反映し他の剛体との衝突を通して動きを反映する。
Bulletでは以下のコードで剛体に追加オプションを指定する。

byRigidBody *RigidBody;

// 剛体の位置更新に物理演算を使わない。
// MotionState::getWorldTransformが毎ステップコールされるようになるのでここで剛体位置を更新する。
RigidBody->setCollisionFlags(RigidBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT );
// 毎ステップの剛体位置通知を無効にする
// MotionState::setWorldTransformの毎ステップ呼び出しが無効になる(剛体位置は判っているので不要)
RigidBody->setActivationState( DISABLE_DEACTIVATION );

モーションステートには毎フレーム剛体位置を更新するためのgetWorldTransformをオーバーライドしたものを作成する。

// class CKinematicsRigidBody : public btMotionStateの一部

btTransform CKinematicsRigidBody::getBoneRigidTransform()const
{
	btTransform boneTransform;
    // ボーン位置を取得
	boneTransform.setFromOpenGLMatrix(&Bone->getLocalMatrix()[0][0]);
    // ボーン位置にボーンローカル座標での剛体重心位置をかける
	return boneTransform * OffsetInBone;
}

/// synchronizes world transform from user to physics
/// m_pBone -> centerOfMassWorldTrans
void CKinematicsRigidBody::getWorldTransform(
		btTransform& centerOfMassWorldTrans) const
{
    // 剛体重心位置を更新
	centerOfMassWorldTrans=getBoneRigidTransform();
}
1:物理演算

bulletの通常の物理演算を実行してその結果でボーン位置を更新する剛体。ボーン位置が更新されるので直接的に頂点位置に影響する。

btDefaultMotionStateで構わないがMotionState::setWorldTransformをオーバーライドしてここでボーン位置を更新することができる。

// class CPhysicsBoneRigidBody : public btMotionStateの一部
// 基本的にはbtDefaultMotionStateと同様でOK

void CPhysicsBoneRigidBody::setWorldTransform(
		const btTransform& centerOfMassWorldTrans)
{
	m_graphicsWorldTrans = centerOfMassWorldTrans ;

	if(Bone){
        // 回転中心を剛体中心からボーンの頭に調整したものでボーンを更新
		(m_graphicsWorldTrans * OffsetInBoneInv)
			.getOpenGLMatrix(&Bone->getMatLocal()[0][0]);
	}
}
2:物理演算(Bone位置合せ)

bulletの通常の物理演算を実行してその結果でボーンの回転のみを更新する剛体(たぶん)。ボーン位置が更新されるので直接的に頂点位置に影響する。

btDefaultMotionStateで構わないがMotionState::setWorldTransformをオーバーライドしてここでボーン位置を更新することができる。

// class CKinematicsMoveAndPhysicsRotateRigidBody : public btMotionStateの一部
// まだ不完全。剛体位置をボーン位置で更新する処理を未実装(ネクタイが連動していない)。
// 剛体回転でボーン回転を更新する方だけ実装。

void CKinematicsMoveAndPhysicsRotateRigidBody::setWorldTransform(
		const btTransform& centerOfMassWorldTrans)
{
	m_graphicsWorldTrans = centerOfMassWorldTrans ;

	if(m_pBone){
		// 回転中心を剛体中心からボーンの頭に調整した変換
		btTransform t=m_graphicsWorldTrans * m_BoneOffsetInv;

                // ボーンの現在位置
		Vector3 v;
		m_pBone->getPos(&v);
		btVector3 boneOffset(v.x, v.y, v.z);

                // 位置をボーンの現在位置で更新
                // 物理演算結果の回転 + ボーンの現在位置への移動となる
		t.setOrigin( boneOffset);

                // ボーンに反映
		t.getOpenGLMatrix(&m_pBone->getMatLocal()[0][0]);
        }
}


さらにプログラム上は第4のタイプがある。
関連ボーンが0xFFFFになっていて連動するボーンが無いものです(位置などはセンターボーン基準にする)。

3:関連ボーンの無い剛体

1, 2で関連するボーンが無い場合になる。これは剛体としては1, 2と同じように物理演算で位置を更新されるが連動するボーンの指定が無い。そのため、この剛体に連動して直接に動く頂点が存在しないということになる。ただし、衝突を通して他の剛体の位置に影響を及ぼす。