剛体表示(修正)


Irrlichtに乗せるにあたってBulletを調べていたら前の剛体表示はミスってたことが判明。
PMDにある3種類の剛体のうちボーン追従タイプが出ていなかった。
Cスタイルのキャストには注意しないといかん。dynamic_castを使っていればすぐに気付いたのに。

btMotionStateについて

http://www.bulletphysics.com/Bullet/BulletFull/classbtMotionState.html
http://bulletphysics.org/mediawiki-1.5.8/index.php/MotionStates

setWorldTransform

最初に初期位置を設定したときとstepSimulation時に呼ばれる。
以下のようにしてkinematic ObjectにするとstepSimulation時には呼ばれなくなるみたいだ。
>|cpp|
btRigidBody *body;
body->setActivationState(DISABLE_DEACTIVATION);
body->setCollisionFlags( body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
|

http://sssiii.seesaa.net/article/139271643.html
これは勝手に動かないが衝突判定だけしたい場合に使うということだろうと思う。

詳細調べ中・・・

getWorldTransform

rigidBodyの位置を更新する。
defaultMotionStateではsetWorldTransformで入ってきたものをそのまま出すだけ。

なんか違う。
ちゃんと理解できてから書き直す。

FKまで実装完了

テクスチャ未処理(目)
足のIKが未解決

ひとまずFKの実装まで完了。
踏んだ地雷について。

core::quaternion::getMatrixがおかしい

今まで見たことのない微妙な歪み具合に2時間くらいはまる。結局Irrlichtの方に問題があったみたい。quaternionから回転行列を得るところで変な値が返ってくるようだ。
誰も使ってないのかしら。
代替コード

static core::matrix4 getMatrix(const core::quaternion &rot)
{
	float xx=rot.X*rot.X;
	float yy=rot.Y*rot.Y;
	float zz=rot.Z*rot.Z;

	float xy=rot.X*rot.Y;
	float yz=rot.Y*rot.Z;
	float zx=rot.Z*rot.X;

	float wx=rot.W*rot.X;
	float wy=rot.W*rot.Y;
	float wz=rot.W*rot.Z;

	core::matrix4 m(core::matrix4::EM4CONST_IDENTITY);
	
	m[0]=1-2*(yy+zz);
	m[1]=2*(xy+wz);
	m[2]=2*(zx-wy);
	m[4]=2*(xy-wz);
	m[5]=1-2*(xx+zz);
	m[6]=2*(yz+wx);
	m[8]=2*(zx+wy);
	m[9]=2*(yz-wx);
	m[10]=1-2*(xx+yy);

	return m;
}

core::vector3df::getInterpolatedがおかしい

境界付近でワープする。
これはすぐわかったが誰も使ってないのかしら。
代替コード。

static core::vector3df getInterpolated(
		const core::vector3df &s, const core::vector3df &e, f32 factor)
{
	return core::vector3df(
			s.X+(e.X-s.X)*factor,
			s.Y+(e.Y-s.Y)*factor,
			s.Z+(e.Z-s.Z)*factor
			);
}

1フレーム目で固まる

IMeshBuffer::setDirty(EBT_VERTEX)
を使わないとキャッシュされるみたいだ。
(SSharedMeshBufferをコンパイル通るようにして使用)


新しい地雷をいくつか踏んで除去したがまぁ順調といったところか。ただ、2コアなのにCPU負荷が両方最大になってやたら重くなる。中でスレッドでも使ってるのだろうか。まだ、単純なソフトウェアスキニングだけなのでそんなに重くならないと思うのだが・・・。さらにOpenGLのバックエンドの方がDirectXのバックエンドよりも重い。あとで、GLSLを使いたいのでこれは困る。頂点配列をキャッシュしているみたいだからその界隈が重いような気がするな。要調査。

irrlichtの描画

CSceneManager::drawAll()内の"// render default objects"で始まるブロックの中

			for (i=0; i<SolidNodeList.size(); ++i)
				SolidNodeList[i].Node->render();

ISceneNode->Renderを呼び出す。
その中で

video::IVideoDriver *driver;
scene::IMeshBuffer *mb;

driver->drawMeshBuffer(mb);

と呼ばれる。
で、drawMeshBufferでハードウェアバッファに関する処理をしている様子。
毎フレームでハードウェアバッファの読み書きをしているのが負担になっていると思われる。

//! Draws a mesh buffer
void CNullDriver::drawMeshBuffer(const scene::IMeshBuffer* mb)
{
	if (!mb)
		return;

	//IVertexBuffer and IIndexBuffer later
	SHWBufferLink *HWBuffer=getBufferLink(mb);

	if (HWBuffer)
		drawHardwareBuffer(HWBuffer);
	else
		drawVertexPrimitiveList(mb->getVertices(), mb->getVertexCount(), mb->getIndices(), mb->getIndexCount()/3, mb->getVertexType(), scene::EPT_TRIANGLES, mb->getIndexType());
}

OpenGLでは
COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::IMeshBuffer* mb)
でvboを作っていた。

このvboを作成しないようににするには以下のようにする。

meshBuffer->setHardwareMappingHint(EHM_NEVER);

これで使うコアがひとつになって速度が普通になった。
ドライバがスレッド使っていたのかもしれない。
IMeshBuffer::setDirty(EBT_VERTEX)
を頻繁に呼び出す場合は注意が必要と。

IKまで完了

テクスチャと表情モーフィングとIKを実装

IKのひざの特別処理で回転をオイラー角に分解してから、角度に制限を加えた後でオイラー角を回転に戻す処理があるのだがそこで使った
core::matrix4::setRotationRadians

core::quaternion::quaternion(const core::matrix4 &rotation)
の動きが怪しかった。Irrlichtの数学ライブラリに疑心暗鬼になりつつw
代替コード。

static core::quaternion constraint(const core::quaternion q)
{
	core::vector3df euler;
	q.toEuler(euler);
	// Xの回転角度の制限
	euler.X=clamp(euler.X, static_cast<f32>(-M_PI), -0.002f);
	// Xの回転のみのクォータニオンを作る
	float radianX=euler.X * 0.5f;
	float sinX = sinf(radianX);
	float cosX = cosf(radianX);
	return core::quaternion(sinX, 0, 0, cosX);
}

x軸1軸の回転だとわかっているのでこんな感じに。
次は、Bullet組み込み。