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