3D:OBBとOBB

OBBです。
バウンディングスフィアの応用で中心位置と半径を使い、
内積と外積を駆使して当たり判定を行います。

font.h

#pragma once
class GLFONT
{
public:
 HFONT Hfont;
 HDC Hdc;
 GLFONT(wchar_t *fontname, int size);
 void DrawStringW(int x,int y,wchar_t *format, ...);
};
//コンストラクタ フォント作成
GLFONT::GLFONT(wchar_t *fontname, int size)
{
 Hfont = CreateFontW(
        size,      //フォント高さ
        0,       //文字幅
        0,       //テキストの角度
        0,       //ベースラインとx軸との角度
        FW_REGULAR,     //フォントの太さ
        FALSE,      //イタリック体
        FALSE,      //アンダーライン
        FALSE,      //打ち消し線
        SHIFTJIS_CHARSET,   //文字セット
        OUT_DEFAULT_PRECIS,   //出力精度
        CLIP_DEFAULT_PRECIS,  //クリッピング精度
        ANTIALIASED_QUALITY,  //出力品質
        FIXED_PITCH | FF_MODERN, //ピッチとファミリー
        fontname);     //書体名

 Hdc = wglGetCurrentDC();
 SelectObject(Hdc, Hfont);
}
//ワイド文字列の描画
void GLFONT::DrawStringW(int x,int y,wchar_t *format, ...)
{
 wchar_t buf[256];
 va_list ap;
 int Length=0;
 int list=0;
 
 //ポインタがNULLの場合は終了
 if ( format == NULL )
  return;

 //文字列変換
 va_start(ap, format);
 vswprintf_s(buf, format, ap);
 va_end(ap);

 Length = wcslen(buf);
 list = glGenLists(Length);
 for( int i=0; i<Length; i++ ){
  wglUseFontBitmapsW(Hdc, buf[i], 1, list + (DWORD)i);
 }

 glDisable(GL_LIGHTING);
 glRasterPos2i(x, y);
 //ディスプレイリストで描画
 for( int i=0; i<Length; i++ )
 {
  glCallList(list + i);
 }
 glEnable(GL_LIGHTING);
 //ディスプレイリスト破棄
 glDeleteLists(list, Length);
 list = 0;
 Length = 0;
}

main.cpp

#pragma comment(linker, "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
#include <GL/freeglut/freeglut.h>

#include <math.h>
#include <string>
#include <iostream>
#include <ctype.h>

#include "font.h"

#define WIDTH 640
#define HEIGHT 480
#define Pos_ADD 10


using namespace std;

BYTE KeyTbl[256];
//ライトの位置
GLfloat lightpos[] = { 200.0, 300.0, 500.0, 1.0 };
const float MTXLIB_PI = 3.14159265f;
float angle_x=0.0f,angle_y=0.0f;
GLFONT *font;

// 角度をラジアンに変換
float DegToRad(float deg)
{
 return deg*MTXLIB_PI/180;
}
//3つのベクトル
struct Vector3f{
 float x;
 float y;
 float z;
 Vector3f(float x=0, float y=0, float z=0){this->x=x; this->y=y; this->z=z;}
  // 内積
 float Dot(Vector3f &vec){
  return (this->x*vec.x + this->y*vec.y + this->z*vec.z);
 }
 // 外積
 Vector3f Cross(Vector3f &vec){
  return Vector3f(
   this->y*vec.z - this->z*vec.y,
   this->z*vec.x - this->x*vec.z,
   this->x*vec.y - this->y*vec.x);
 }
}vec3d;

Vector3f & operator+(Vector3f &a,Vector3f &b){
 a.x+=b.x;
 a.y+=b.y;
 a.z+=b.z;
 return a;
}

Vector3f & operator-(Vector3f &a,Vector3f &b){
 a.x-=b.x;
 a.y-=b.y;
 a.z-=b.z;
 return a;
}

//4つのベクトル
struct Vector4f{
 union {
        struct {
   float x;
   float y;
   float z;
   float w;
  };
  float Index[4];
 };
 Vector4f(){};
 Vector4f(float _x,float _y,float _z,float _w){
  x=_x;y=_y;z=_z;w=_w;
 };
};

//マトリクス構造体
struct MATRIX {
    union {
        struct {
            float _11, _12, _13, _14;
            float _21, _22, _23, _24;
            float _31, _32, _33, _34;
            float _41, _42, _43, _44;
        };
        float mat_4x4[4][4];
        float mat_16[16];
    };
 MATRIX(){//単位行列に初期化
  for(int i=0;i<16;i++){
   this->mat_16[i]=0;
  }
  this->_11=this->_22=this->_33=this->_44=1;
 }
 void PRINT(char* text){
  printf("%s\n%f,%f,%f,%f\n%f,%f,%f,%f\n%f,%f,%f,%f\n%f,%f,%f,%f\n\n",
   text,
   this->_11,this->_21,this->_31,this->_41,
   this->_12,this->_22,this->_32,this->_42,
   this->_13,this->_23,this->_33,this->_43,
   this->_14,this->_24,this->_34,this->_44);
 }
 MATRIX Multiplication(MATRIX& mat);//合成

 Vector3f operator *(Vector3f &v){
  Vector3f ret;
  ret.x = (v.x * this->_11) + (v.y * this->_21) + (v.z * this->_31) + (this->_41);
  ret.y = (v.x * this->_12) + (v.y * this->_22) + (v.z * this->_32) + (this->_42);
  ret.z = (v.x * this->_13) + (v.y * this->_23) + (v.z * this->_33) + (this->_43);
  return ret;
 }
  // 回転行列に変換
 MATRIX Rotate(char axis, float rad){
  MATRIX ret;
  float sinA = sinf(rad);
  float cosA = cosf(rad);
  switch (axis){
  case 'x':
  case 'X':
   ret._11 =  1.0f; ret._21 =  0.0f; ret._31 =  0.0f;
   ret._12 =  0.0f; ret._22 =  cosA; ret._32 = -sinA;
   ret._13 =  0.0f; ret._23 =  sinA; ret._33 =  cosA;
   break;

  case 'y':
  case 'Y':
   ret._11 =  cosA; ret._21 =  0.0f; ret._31 =  sinA;
   ret._12 =  0.0f; ret._22 =  1.0f; ret._32 =  0.0f;
   ret._13 = -sinA; ret._23 =  0.0f; ret._33 =  cosA;
   break;

  case 'z':
  case 'Z':
   ret._11 =  cosA; ret._21 = -sinA; ret._31 =  0.0f;
   ret._12 =  sinA; ret._22 =  cosA; ret._32 =  0.0f;
   ret._13 =  0.0f; ret._23 =  0.0f; ret._33 =  1.0f;
   break;
  }

  ret._14 = 0.0f; ret._24 = 0.0f; ret._34 = 0.0f;
  ret._41 = 0.0f;
  ret._42 = 0.0f;
  ret._43 = 0.0f;
  ret._44 = 1.0f;

  return ret;
 }
  // YawPitchRollを指定して回転行列を取得
 MATRIX YawPitchRoll(float y, float x, float z)
 {
  MATRIX ret;
  MATRIX mY = ret.Rotate('y', y);
  MATRIX mX = ret.Rotate('x', x);
  MATRIX mZ = ret.Rotate('z', z);
  MATRIX a=mX.Multiplication(mY);
  MATRIX b=mZ.Multiplication(a);
  return b;
 }
};
//合成
MATRIX MATRIX::Multiplication(MATRIX& mat)
{
 MATRIX ret;
 for(int y=0;y<4;y++){
  for(int x=0;x<4;x++){
   ret.mat_16[y*4+x]=mat.mat_16[y*4]*this->mat_16[x]+mat.mat_16[y*4+1]*this->mat_16[x+4]+mat.mat_16[y*4+2]*this->mat_16[x+8]+mat.mat_16[y*4+3]*this->mat_16[x+12];
  }
 }
 return ret;
}


//直方体構造体
struct Cuboid{
 GLfloat Color[4];//色
 Vector3f Radius;//半径
 Vector3f Pos;//中心位置
 Vector3f Rotate;    // 回転角度
 Vector3f axisX;  // 分離軸X
 Vector3f axisY;  // 分離軸Y
 Vector3f axisZ;  // 分離軸Z
 void Draw(int color);
 Vector3f GetMinVec3();
 Vector3f GetMaxVec3();
 // 分離軸を更新
 void UpdateAxisAll()
 {
  MATRIX mRot = MATRIX().YawPitchRoll(DegToRad(Rotate.y), DegToRad(Rotate.x), DegToRad(Rotate.z));
  axisX = mRot*Vector3f(1, 0, 0);
  axisY = mRot*Vector3f(0, 1, 0);
  axisZ = mRot*Vector3f(0, 0, 1);
 }
};
void Cuboid::Draw(int color){
 // シーンの描画
 static GLfloat red[]   = { 1.0f, 0.0f, 0.0f, 1.0f };
 static GLfloat gleen[] = { 0.2f, 0.8f, 0.2f, 1.0f };
 static GLfloat white[] = { 0.8f, 0.8f, 0.8f, 1.0f };
 GLfloat *c;
 if(color == 0)c = red;
 if(color == 1)c = gleen;
 if(color == 2)c = white;

 // 箱描画
 glPushMatrix();
 {
  glTranslatef(this->Pos.x, this->Pos.y, this->Pos.z);
  glRotatef(this->Rotate.z, 0, 0, 1);
  glRotatef(this->Rotate.x, 1, 0, 0);
  glRotatef(this->Rotate.y, 0, 1, 0);
  glScaled(this->Radius.x*2, this->Radius.y*2, this->Radius.z*2);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, c);
  glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
  glutSolidCube(1);
 }
 glPopMatrix();
}


Vector3f Cuboid::GetMinVec3(){
 vec3d.x=Pos.x-Radius.x;
 vec3d.y=Pos.y-Radius.y;
 vec3d.z=Pos.z-Radius.z;
 return(vec3d);
}
Vector3f Cuboid::GetMaxVec3(){
 vec3d.x=Pos.x+Radius.x;
 vec3d.y=Pos.y+Radius.y;
 vec3d.z=Pos.z+Radius.z;
 return(vec3d);
}

 

void Line3D(float x1,float y1,float z1,float x2,float y2,float z2){
  //線幅
  glLineWidth(1.0);
  //線
  glBegin(GL_LINES);
 glVertex3f(x1,y1,z1);
 glVertex3f(x2,y2,z2);
  glEnd();
}

void DrawMeasure(int measure,float size){
 glDisable(GL_LIGHTING);
 glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
 for(int x=0;x<=measure;x++){Line3D(x*size-(size*measure/2),0,-(size*measure/2),x*size-(size*measure/2),0,measure*size-(size*measure/2));}
 for(int y=0;y<=measure;y++){Line3D(-(size*measure/2),0,y*size-(size*measure/2),measure*size-(size*measure/2),0,y*size-(size*measure/2));}
 glDisable(GL_DEPTH_TEST);
 glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
 Line3D(0,0,0,(measure/2+2)*size,0,0);
 glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
 Line3D(0,0,0,0,(measure/2+2)*size,0);
 glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
 Line3D(0,0,0,0,0,(measure/2+2)*size);
 glEnable(GL_LIGHTING);
 glEnable(GL_DEPTH_TEST);
}


bool CompareLengthOBB(Cuboid&, Cuboid&, Vector3f, Vector3f);

//境界箱(OBB)による当たり判定
bool IsCollideBoxOBB(Cuboid cA, Cuboid cB)
{
 // 中心間の距離ベクトル算出
 Vector3f vDistance = cB.Pos - cA.Pos;

 // 分離軸更新
 cA.UpdateAxisAll();
 cB.UpdateAxisAll();

 // 分離軸を比較
 if(!CompareLengthOBB(cA, cB, cA.axisX, vDistance)) return false;
 if(!CompareLengthOBB(cA, cB, cA.axisY, vDistance)) return false;
 if(!CompareLengthOBB(cA, cB, cA.axisZ, vDistance)) return false;
 if(!CompareLengthOBB(cA, cB, cB.axisX, vDistance)) return false;
 if(!CompareLengthOBB(cA, cB, cB.axisY, vDistance)) return false;
 if(!CompareLengthOBB(cA, cB, cB.axisZ, vDistance)) return false;

 // 分離軸同士の外積を比較
 Vector3f vSep;
 vSep = cA.axisX.Cross(cB.axisX);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisX.Cross(cB.axisY);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisX.Cross(cB.axisZ);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisY.Cross(cB.axisX);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisY.Cross(cB.axisY);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisY.Cross(cB.axisZ);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisZ.Cross(cB.axisX);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisZ.Cross(cB.axisY);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;
 vSep = cA.axisZ.Cross(cB.axisZ);
 if(!CompareLengthOBB(cA, cB, vSep, vDistance)) return false;

 return true;
}

//OBBの投影距離比較
bool CompareLengthOBB(Cuboid &cA, Cuboid &cB, Vector3f vSep, Vector3f vDistance)
{
 // 分離軸上のAからBの距離
 float length = fabsf(vSep.Dot(vDistance));

 // 分離軸上でAから最も遠いAの頂点までの距離
 float lenA =
    fabsf(cA.axisX.Dot(vSep)*cA.Radius.x)
    + fabsf(cA.axisY.Dot(vSep)*cA.Radius.y)
    + fabsf(cA.axisZ.Dot(vSep)*cA.Radius.z);

 // 分離軸上でBから最も遠いBの頂点までの距離
 float lenB =
    fabsf(cB.axisX.Dot(vSep)*cB.Radius.x)
    + fabsf(cB.axisY.Dot(vSep)*cB.Radius.y)
    + fabsf(cB.axisZ.Dot(vSep)*cB.Radius.z);
 if(length > lenA + lenB)
 {
  return false;
 }
 return true;
}

Cuboid self;   // 箱1(動く)
Cuboid target; // 箱2(動かない)

bool bHit;    // 当たりフラグ


void Init(void)
{
 // 初期設定
 glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
 glEnable(GL_DEPTH_TEST);
 glEnable(GL_LIGHTING);
 glEnable(GL_LIGHT0);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
 glEnable(GL_BLEND);//ブレンドの有効化


 // 箱を作る
 self.Pos      = Vector3f(1, 0, 0);
 self.Radius   = Vector3f(0.4f, 0.5f, 0.6f);
 self.Rotate      = Vector3f(0, 0, 0);
 target.Pos    = Vector3f(0, 0, 0);
 target.Radius = Vector3f(0.5f, 0.6f, 0.4f);
 target.Rotate    = Vector3f(0, 0, 0);

 angle_x=angle_y=0;
 if(font==NULL)font = new GLFONT(L"MS明朝", 12);

 bHit = false;
}

 

void display(void)
{
 // 画面クリア
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glViewport(0, 0, WIDTH, HEIGHT);
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 //視野角,アスペクト比(ウィンドウの幅/高さ),描画する範囲(最も近い距離,最も遠い距離)
 gluPerspective(30.0, (double)WIDTH / (double)HEIGHT, 1.0, 2000.0);

 // 視点の移動
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
  //視点の設定
 gluLookAt(0.0,6.0,10.0, //カメラの座標
      0.0,0.0,0.0, // 注視点の座標
     0.0,1.0,0.0); // 画面の上方向を指すベクトル

 glRotatef(angle_y, 0, 1, 0);
 glRotatef(angle_x, 1, 0, 0);

 DrawMeasure(16,1);
 glLightfv(GL_LIGHT0, GL_POSITION, lightpos);


 // 箱の描画
 if(bHit) self.Draw(0);
 else     self.Draw(2);
 target.Draw(1);

 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
 glOrtho(0, WIDTH, HEIGHT, 0, -1, 1);

 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

 font->DrawStringW(16,32,L"操作方法");
 font->DrawStringW(16,64,L"初期化:スペースキー、リターンキー");
 font->DrawStringW(16,80,L"終了:ESCキー");
 font->DrawStringW(16,112,L"カメラ回転");
 font->DrawStringW(16,128,L"カーソルキー上:カメラX軸+");
 font->DrawStringW(16,144,L"カーソルキー下:カメラX軸-");
 font->DrawStringW(16,160,L"カーソルキー左:カメラY軸+");
 font->DrawStringW(16,176,L"カーソルキー右:カメラY軸-");
 font->DrawStringW(16,208,L"箱移動");
 font->DrawStringW(16,224,L"テンキー8:Y座標-");
 font->DrawStringW(16,240,L"テンキー2:Y座標+");
 font->DrawStringW(16,256,L"テンキー6:X座標+");
 font->DrawStringW(16,272,L"テンキー4:X座標-");
 font->DrawStringW(16,288,L"+キー:Z座標+");
 font->DrawStringW(16,304,L"-キー:Z座標-");
 font->DrawStringW(16,336,L"箱回転");
 font->DrawStringW(16,352,L"テンキー9:Y回転-");
 font->DrawStringW(16,368,L"テンキー1:Y回転+");
 font->DrawStringW(16,384,L"テンキー3:X回転+");
 font->DrawStringW(16,400,L"テンキー7:X回転-");
 font->DrawStringW(16,416,L"*キー:X回転-");
 font->DrawStringW(16,432,L"/キー:X回転-");

 font->DrawStringW(400,32,L"Hitすると赤色になります。");

 glutSwapBuffers();
}


void idle() 
{
 float mov = 0.1f;
 float rot = 1;

 GetKeyboardState(KeyTbl);
 if(KeyTbl[VK_ESCAPE] & 0x80)exit(0);//終了
 if(KeyTbl[VK_RETURN] & 0x80)Init();//初期化
 if(KeyTbl[VK_SPACE] & 0x80)Init();//初期化
 //カメラ移動
 if(KeyTbl[VK_LEFT] & 0x80)angle_y+=1.0f;//左キー
 if(KeyTbl[VK_UP] & 0x80)angle_x-=1.0f;//上キー
 if(KeyTbl[VK_RIGHT] & 0x80)angle_y-=1.0f;//右キー
 if(KeyTbl[VK_DOWN] & 0x80)angle_x+=1.0f;//下キー

 //箱移動
 if(KeyTbl[VK_NUMPAD8] & 0x80)self.Pos.y -= mov;//Y座標-
 if(KeyTbl[VK_NUMPAD2] & 0x80)self.Pos.y += mov;//Y座標+
 if(KeyTbl[VK_NUMPAD6] & 0x80)self.Pos.x += mov;//X座標+
 if(KeyTbl[VK_NUMPAD4] & 0x80)self.Pos.x -= mov;//X座標-
 if(KeyTbl[VK_ADD] & 0x80)self.Pos.z += mov;//Z座標+
 if(KeyTbl[VK_SUBTRACT] & 0x80)self.Pos.z -= mov;//Z座標-

 //箱回転
 if(KeyTbl[VK_NUMPAD9] & 0x80)self.Rotate.y -= rot;//Y回転-
 if(KeyTbl[VK_NUMPAD1] & 0x80)self.Rotate.y += rot;//Y回転+
 if(KeyTbl[VK_NUMPAD3] & 0x80)self.Rotate.x += rot;//X回転+
 if(KeyTbl[VK_NUMPAD7] & 0x80)self.Rotate.x -= rot;//X回転-
 if(KeyTbl[VK_MULTIPLY] & 0x80)self.Rotate.z += rot;//Z回転+
 if(KeyTbl[VK_DIVIDE] & 0x80)self.Rotate.z -= rot;//Z回転-


 if(self.Rotate.x < 0)   self.Rotate.x += 360;
 if(self.Rotate.x > 360) self.Rotate.x -= 360;
 if(self.Rotate.y < 0)   self.Rotate.y += 360;
 if(self.Rotate.y > 360) self.Rotate.y -= 360;
 if(self.Rotate.z < 0)   self.Rotate.z += 360;
 if(self.Rotate.z > 360) self.Rotate.z -= 360;

 // 当たり判定
 if(IsCollideBoxOBB(self, target)){
  bHit = true;
 }else{
  bHit = false;
 }


 glutPostRedisplay();

}


int main(int argc, char *argv[])
{
 glutInitWindowPosition(100, 100);
 glutInitWindowSize(WIDTH, HEIGHT);
 glutInit(&argc, argv);
 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
 glutCreateWindow("OBB");
 glutDisplayFunc(display);
 glutIdleFunc(idle);
 Init();
 glutMainLoop();
 return 0;
}

 

 

 

 

 

 

最終更新:2013年11月01日 18:07
添付ファイル