高速にラフな当たり判定を行うためのバウンディングシリンダー

クロスシミュレーションできちんと体との当たり判定を処理する場合、これを例えば当たり判定用簡易モデルなんかと交差判定するとなると大変なことになる。理由としてまず第一に簡易モデルを現在の体勢に頂点を変換するのに時間が掛かる。第二に処理が必要なクロスの方の頂点の方が膨大すぎる。第三に交差判定自体時間がかかる。そこでバウンディングスフィアとかシリンダーとかを人為的にあたりがちな場所に設定してやる。人体はどちらかというと球よりも円柱の方が近似しやすい。高速なシリンダーっぽい当たり判定の計算の仕方があるので内緒で教えちゃう。

struct collision_cylinder
{
	D3DXVECTOR3 pos; // 片方の円柱の端
	D3DXVECTOR3 vec; // 円柱のもう片方までを結んだベクトル ノルムは円柱高さ
	float radius; // 円柱半径
	float radiussq;
	float length; // 円柱の高さ
	float lengthsq;
	bool collide_point(const D3DXVECTOR3& targetpos, D3DXVECTOR3& out);
};

// true 点は内側にある 引数outはその点をシリンダーの外側へ押し出した位置が書き出される
// false 点は外側にある 引数outは更新されない
bool collision_cylinder::collide_point(
	const D3DXVECTOR3& targetpos, 
	D3DXVECTOR3& out
)
{
	D3DXVECTOR3 targetvec(targetpos - pos);
	float dot = D3DXVec3Dot(&targetvec, &vec);
	if(dot < 0.0f || dot > lengthsq) return false; // シリンダー上下の範囲を超える

	// targetposからcylinder中心へ落とした垂線nの長さの自乗
	float nsq = 
	(targetvec.x * targetvec.x + targetvec.y * targetvec.y + targetvec.z * targetvec.z)
	- (dot * dot) / lengthsq;
	
	if(nsq > radiussq) return false;

	// 押しだす位置である n と円周の交点を求める
	D3DXVECTOR3 veccyl(vec * (dot / lengthsq)); // 垂線到達点までのcylinder中心ベクトル 
	D3DXVECTOR3 vecn(targetvec - veccyl);
	vecn *= (1.0001f * radius) / MyVec3Length(vecn); // 延長

	out = pos + veccyl + vecn;
	return true;
}

( MyVec3Lengthはベクトルの長さを返す関数 sqrt(x*x+y*y+z*z) )

厳密なシリンダーでなくドデカイ球体を円柱状に切り出したような形になるのがわかるだろうか。この近似形状のおかげで無関係な点を早めに切り捨てることができる。これを太ももとかに一本設定すればスカートの裾が当たる太ももの当たり判定モデルはわずか2つの頂点で表すことができる。