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; }