OpenGLで境界球判定

境界球判定

ようやくOpenGLが分かってきたような、分からないような…(∀`;
 
とりあえず、境界球判定を行うサンプルを。
 

赤ボールの移動

Z・Xキー:X方向への移動
A・Sキー:Y方向への移動
Q・Wキー:Z方向への移動

カメラの移動

↑↓キー:X軸回転
←→キー:Y軸回転
 

ソースコード
#include <iostream>
#include <glut.h>
#include <ctype.h>
#include <math.h>

using namespace std;


/**
 * 3次元ベクトル構造体
 */
struct Vec3
{
	float x, y, z;
	Vec3(float x=0, float y=0, float z=0){this->x=x; this->y=y; this->z=z;}
	Vec3 operator +(Vec3 &vec)           {return Vec3(this->x+vec.x, this->y+vec.y, this->z+vec.z);}
	Vec3 operator -(Vec3 &vec)           {return Vec3(this->x-vec.x, this->y-vec.y, this->z-vec.z);}
	Vec3 operator *(float f)             {return Vec3(this->x*f, this->y*f, this->z*f);}
	Vec3 operator /(float f)             {return Vec3(this->x/f, this->y/f, this->z/f);}
	Vec3 operator =(Vec3 &vec)           {return Vec3(this->x=vec.x, this->y=vec.y, this->z=vec.z);}
	Vec3 operator ==(Vec3 &vec)          {return (this->x==vec.x && this->y==vec.y && this->z==vec.z);}
	Vec3 operator !=(Vec3 &vec)          {return (this->x!=vec.x || this->y!=vec.y || this->z!=vec.z);}
	void Set(float x, float y, float z)  {this->x=x; this->y=y; this->z=z;}
	// スカラの二乗を求める
	float LengthSq() {return x*x + y*y + z*z;}
	// スカラ
	float Length() {return sqrt(LengthSq());}
	// 正規化
	Vec3 Normalize() {
		float m = LengthSq();
		if (m > 0.0f) m = 1.0f / m;
		else          m = 0.0f;
		return Vec3(x*m, y*m, z*m);
	}
	// 内積
	float Dot(Vec3 &vec)
	{
		return (this->x*vec.x + this->y*vec.y + this->z*vec.z);
	}
	// 外積
	Vec3 Cross(Vec3 &vec)
	{
		return Vec3(
			this->y*vec.z - this->z*vec.y,
			this->z*vec.x - this->x*vec.z,
			this->x*vec.y - this->y*vec.x);
	}
};

// 境界球判定
inline bool CollideSphere(Vec3 &v1, Vec3 &v2, float r1, float r2)
{
	Vec3 vTmp = v1-v2;
	return (vTmp.Dot(vTmp) < (r1+r2)*(r1+r2));
};

/**
 * 球構造体
 */
struct TSphere
{
	Vec3  pos;    // 中心座標
	float radius; // 半径
};
TSphere self;   // 球1(コイツが動く)
TSphere target; // 球2(コイツは動かない)

Vec3 vEye;    // カメラ座標

// 球の描画
void DrawSphere(Vec3, float, int);

/**
 * 初期化
 */
void Init(void)
{
	// 初期設定
	glClearColor(0.0, 0.0, 1.0, 0.0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHT0);

	// ボールを作る
	self.pos      = Vec3(1, 1, 0);
	self.radius   = 0.5f;
	target.pos    = Vec3(0, 0, 0);
	target.radius = 0.5f;

	// カメラ座標初期化
	vEye    = Vec3(0, 0, -5);
}

/**
 * キーが押されたときの処理
 * @param key  押下キー
 * @param x, y 座標
 */
void InputKey(unsigned char key, int x, int y)
{
	// 動かす
	float mov = 0.1f;
	switch(toupper(key))
	{
	case 'Z':
		self.pos.x -= mov;
		break;
	case 'X':
		self.pos.x += mov;
		break;
	case 'A':
		self.pos.y -= mov;
		break;
	case 'S':
		self.pos.y += mov;
		break;
	case 'Q':
		self.pos.z -= mov;
		break;
	case 'W':
		self.pos.z += mov;
		break;
	case 0x1b:	// ESCキー
		// プログラムを終了
		exit(0);
		break;
	}

	// ヒットチェック
	if(CollideSphere(self.pos, target.pos, self.radius, target.radius))
	{
		cout << "隊長!当たってます!!" << endl;
	}
	else
	{
		cout << "当たってません。" << endl;
	}
}

/**
 * 特殊キーが押されたときの処理
 * @param key  押下キー
 * @param x, y 座標
 */
void InputKeySp(int key, int x, int y)
{
	// カメラ移動
	Vec3 vAxis;
	Vec3 vN;
	switch(key)
	{
	case GLUT_KEY_UP:
		// 上キー
		vAxis = Vec3(1, 0, 0);
		break;
	case GLUT_KEY_DOWN:
		// 下キー
		vAxis = Vec3(-1, 0, 0);
		break;
	case GLUT_KEY_LEFT:
		// 左キー
		vAxis = Vec3(0, 1, 0);
		break;
	case GLUT_KEY_RIGHT:
		// 右キー
		vAxis = Vec3(0, -1, 0);
		break;
	}
	vN = vEye.Normalize().Cross(vAxis);
	vEye = vEye + vN;
}

/**
 * 描画
 */
void Display(void)
{
	// 画面クリア
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// 視点の移動
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(vEye.x, vEye.y, vEye.z, 0, 0, 0, 0, 1, 0);

	// 球の描画
	DrawSphere(self.pos, self.radius, 0);
	DrawSphere(target.pos, target.radius, 1);

	// 地面を線画で描く
	glColor3d(0.0, 0.0, 0.0);
	glBegin(GL_LINES);
	{
		for (int i = -10; i <= 10; i++)
		{
			glVertex3d((GLdouble)i, -0.5, -10.0);
			glVertex3d((GLdouble)i, -0.5,  10.0);
			glVertex3d(-10.0, -0.5, (GLdouble)i);
			glVertex3d( 10.0, -0.5, (GLdouble)i);
		}
	}
	glEnd();

	glutSwapBuffers();
}

/**
 * ウィンドウのリサイズ
 */
void Resize(int w, int h)
{
	// ウィンドウ全体をビューポートにする
	glViewport(0, 0, w, h);

	// 透視変換行列を設定する
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0);

	// モデルビュー変換行列を指定しておく
	glMatrixMode(GL_MODELVIEW);
}

/**
 * アイドル時
 */
void Idle()	
{
	glutPostRedisplay();
}

/**
 * 球の描画
 * @param pos    描画座標
 * @param radius 半径
 * @param color  描画色(0=red, 1=green, その他=blue)
 */
void DrawSphere(Vec3 pos, float radius, int color)
{
	// シーンの描画
	static GLfloat red[]   = { 0.8, 0.2, 0.2, 1.0 };
	static GLfloat gleen[] = { 0.2, 0.8, 0.2, 1.0 };
	static GLfloat blue[]  = { 0.2, 0.2, 0.8, 1.0 };
	GLfloat *c;
	if(color == 0)      c = red;
	else if(color == 1) c = gleen;
	else                c = blue;

	// 陰影付けをONにする
	glEnable(GL_LIGHTING);
	// 球描画
	glPushMatrix();
	{
		glTranslated(pos.x, pos.y, pos.z);
		glMaterialfv(GL_FRONT, GL_DIFFUSE, c);
		glutSolidSphere(radius, 20, 20);
	}
	glPopMatrix();
	// 陰影付けをOFFにする
	glDisable(GL_LIGHTING);
}

/**
 * メイン関数
 */
int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
	glutCreateWindow(argv[0]);
	glutDisplayFunc(Display);
	glutReshapeFunc(Resize);
	// キーボード入力用関数を登録
	glutKeyboardFunc(InputKey);
	glutSpecialFunc(InputKeySp);
	glutIdleFunc(Idle);
	Init();
	// メインループ
	glutMainLoop();
	return 0;
}