[OpenGL]基于AABB包围盒的漫游时视点与场景的碰撞检测

        在不少游戏中,我们都可能会出现这样的场景,我们在一个建筑内行走,这个建筑有房间也有走廊,这时候在漫游中就有这样一个要求:人不能穿墙。当人物不被绘制而是以人物视野来代替时,这个问题就抽象为漫游时视点与场景碰撞检测的问题,可以看出它和标准的碰撞检测是有区别的,一般的碰撞检测问题考虑的是两个包围盒是否存在交,而在这里我们把可以把问题变得更容易,判断视线与包围盒是否存在交,如果存在,进行怎样的修补保证不穿墙,因为我们可以发现,当视线和包围盒产生交点的时候,人就已经或者即将穿墙。同样的思想在光线追踪算法中也是适用的,我们也是给物体先做一个包围盒,然后再与光线求交。

        而作为编程练习而言,我把这个问题又更进一步的简化了:使用以轴向平行的线段为边的AABB包围盒,忽略y轴信息(因为我们不需要检测是否会穿过天花板和地板),转换为二维的碰撞检测。这个时候,我们事实上判断线线是否相交即可,也就是视线和包围盒正方形的四个边分别判断是否相交。在算法导论中已经给出了判断线段相交的快速算法,在这里我们可以直接使用。

        我们知道求交是一个比较麻烦的运算,当然在这里我们做的并不是求交,而是判断是否存在交点,对于计算机而言,判断是与否相比起说出是什么,要容易得多。尽管如此,这个算法依然非常糟糕,在这里我考虑到了8种一般情况和4种特殊情况,但是这些情况的判定不是完全独立的,它们之间有很小的交集,但正是这样的交集,导致情况的错误识别,整个算法在某些比较特殊的方位会穿墙,或者会出现视角跳变。这些问题都是可以通过多进行分类讨论解决,在这里暂时没有做出修正。

         同样,在性能上,我们还可以做很多优化,首先,我们发现碰撞检测是在人物移动的每个按键响应事件中执行的,所有的物体无论和视点隔得有多远,我们都会进行碰撞检测的计算,这其中的计算量不能算少,更为聪明的做法应当是首先进行一个粗略的排查,把不太可能发生碰撞的物体剔除掉,后续不进行相关运算,在这里我们需要借助一些用于场景管理的数据结构,如八叉树,四叉树等等,思想是递归的二分(或更多)划分空间,最终确定物体所在空间。这里只是一个改进的方案,并没有具体实现。

       

         当然,在这里,包围盒有内外两种,一种是我们走不出,一种是我们走不进,对于一个房间而言,那么我们应该是走不出(不考虑门),对于一个物体而言,我们应该是走不进,在这里走不出的碰撞检测实现起来比较容易,因为它的四个面不会产生干扰,不过在这里我把它写的更简单了,不考虑人物当前方向直接纠正方向为轴向,所以当人物斜撞墙时会自动纠正为贴墙走。而走不进的碰撞检测则留意改进了这一点。

        以下讨论第二种碰撞检测:


           8种一般情况:


           //视线与A面线段相交

           1.前进碰撞A面

           2.后退碰撞A面

           //视线与B面线段相交

           3.前进碰撞B面

           4.后退碰撞B面

           //视线与C面线段相交

           5.前进碰撞C面

           6.后退碰撞C面

           //视线与D面线段相交

           7.前进碰撞D面

           8.后退碰撞D面

4种特殊情况:


             不与任何线相交,平行进入物体内部(因为存在浮点误差,这里的平行也是相对的概念)

             1.平行于z轴进入A面

             2.平行于z轴进入D面

             3.平行于x轴进入C面

             4.平行于x轴进入B面

        

         为什么会出现这四种特殊情况?因为我们可能在平行的过程中左右移动,这时我们会闯入物体,但是由于移动是离散的,所以我们的视线不一定与物体相交!

         那么,显然易见,当碰撞发生后,我们要做的就是“恢复现场”,也就是说,当视线与边缘相交时,我们把它恢复到恰好不相交的位置,如下:

         在这个过程中,我们维护角度不发生变化。

       


        注:操作方法 - WSAD控制上下左右移动 IKJL控制视角上下左右转变,随便找3张贴图 代码即可运行。

test.h

#pragma once
#define GLUT_DISABLE_ATEXIT_HACK
#include "GL/GLUT.H"
void drawRect(GLuint texture);
void drawCube(GLuint texture);
void loadTex(int i, char *filename, GLuint* texture);
typedef enum { left, right, front, back }direction;
void move(float* eye, float* center, direction dir);
void rotate(float* eye, float* center, direction dir);
void inCollisionTest(float* eye, float* center, float x1, float z1,
	float x2, float z2);
void outCollisionTest(float* eye, float* center, float x1, float z1,
	float x2, float z2);

texture.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
#include"test.h"
#define BITMAP_ID 0x4D42 

//读纹理图片  
static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{

	FILE *filePtr;    // 文件指针  
	BITMAPFILEHEADER bitmapFileHeader;    // bitmap文件头  
	unsigned char    *bitmapImage;        // bitmap图像数据  
	int    imageIdx = 0;        // 图像位置索引  
	unsigned char    tempRGB;    // 交换变量  

								 // 以“二进制+读”模式打开文件filename   
	filePtr = fopen(filename, "rb");
	if (filePtr == NULL) {
		printf("file not open\n");
		return NULL;
	}
	// 读入bitmap文件图  
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
	// 验证是否为bitmap文件  
	if (bitmapFileHeader.bfType != BITMAP_ID) {
		fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n");
		return NULL;
	}
	// 读入bitmap信息头  
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
	// 将文件指针移至bitmap数据  
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
	// 为装载图像数据创建足够的内存  
	bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage];
	// 验证内存是否创建成功  
	if (!bitmapImage) {
		fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
		return NULL;
	}

	// 读入bitmap图像数据  
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);
	// 确认读入成功  
	if (bitmapImage == NULL) {
		fprintf(stderr, "Error in LoadBitmapFile: memory error\n");
		return NULL;
	}
	//由于bitmap中保存的格式是BGR,下面交换R和B的值,得到RGB格式  
	for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
		tempRGB = bitmapImage[imageIdx];
		bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
		bitmapImage[imageIdx + 2] = tempRGB;
	}
	// 关闭bitmap图像文件  
	fclose(filePtr);

	return bitmapImage;
}

//加载纹理的函数  
void loadTex(int i, char *filename, GLuint* texture)
{
	
	BITMAPINFOHEADER bitmapInfoHeader;                                 // bitmap信息头  
	unsigned char*   bitmapData;                                       // 纹理数据  

	bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader);
	glBindTexture(GL_TEXTURE_2D, texture[i]);
	// 指定当前纹理的放大/缩小过滤方式  
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glTexImage2D(GL_TEXTURE_2D,
		0,         //mipmap层次(通常为,表示最上层)   
		GL_RGB,    //我们希望该纹理有红、绿、蓝数据  
		bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2   
		bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2   
		0, //边框(0=无边框, 1=有边框)   
		GL_RGB,    //bitmap数据的格式  
		GL_UNSIGNED_BYTE, //每个颜色数据的类型  
		bitmapData);    //bitmap数据指针  

}

test.cpp

#include<stdio.h>
#include<math.h>
#include"test.h"

const float PI = 3.1415926536f;

float min(float x, float y)
{
	return x < y ? x : y;
}

float max(float x, float y)
{
	return x > y ? x : y;
}

struct dot {
	float x;
	float y;
	dot(float _x, float _y) :x(_x), y(_y) { }
};

double Direction(dot pi, dot pj, dot pk) {
	return (pk.x - pi.x)*(pj.y - pi.y) - (pj.x - pi.x)*(pk.y - pi.y);
}

bool OnSegment(dot pi, dot pj, dot pk) {
	if ((min(pi.x, pj.x) <= pk.x) && (pk.x <= max(pi.x, pj.x))
		&& (min(pi.y, pj.y) <= pk.y) && (pk.y <= max(pi.y, pj.y)))
		return true;
	else return false;
}

bool SegmentIntersect(dot p1, dot p2, dot p3, dot p4)
{
	int d1, d2, d3, d4;
	d1 = Direction(p3, p4, p1);
	d2 = Direction(p3, p4, p2);
	d3 = Direction(p1, p2, p3);
	d4 = Direction(p1, p2, p4);
	if (((d1>0 && d2<0) || (d1<0 && d2>0)) && ((d3>0 && d4<0) || (d3<0 && d4>0)))
		return true;
	else if (d1 == 0 && OnSegment(p3, p4, p1))
		return true;
	else if (d2 == 0 && OnSegment(p3, p4, p2))
		return true;
	else if (d3 == 0 && OnSegment(p1, p2, p3))
		return true;
	else if (d4 == 0 && OnSegment(p1, p2, p4))
		return true;
	else return false;
}

float abs(float x)
{
	return x >= 0 ? x : -x;
}

void move(float* eye, float* center, direction dir)
{
	const float d = 1.0f;
	float x1, x2, y1, y2, x, y;
	x1 = eye[0], y1 = eye[2], x2 = center[0], y2 = center[2];
	float len = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
	if (x2 - x1<0.1f&&x2 - x1>-0.1f)x2 = x1;
	if (y2 - y1 < 0.1f&&y2 - y1>-0.1f)y2 = y1;
	switch (dir) {
	case front: {
		eye[0] = d*(x2 - x1) / len + x1;
		eye[2] = d*(y2 - y1) / len + y1;
		center[0] = eye[0] + x2 - x1;
		center[2] = eye[2] + y2 - y1;
		break;
	}
	case back: {
		eye[0] = d*(x1 - x2) / len + x1;
		eye[2] = d*(y1 - y2) / len + y1;
		center[0] = eye[0] + x2 - x1;
		center[2] = eye[2] + y2 - y1;
		break;
	}
	case left: {
		eye[0] = d*(y2 - y1) / len + x1;
		eye[2] = d*(x1 - x2) / len + y1;
		center[0] = eye[0] + x2 - x1;
		center[2] = eye[2] + y2 - y1;
		break;
	}
	case right: {
			eye[0] = d*(y1 - y2) / len + x1;
			eye[2] = d*(x2 - x1) / len + y1;
			center[0] = eye[0] + x2 - x1;
			center[2] = eye[2] + y2 - y1;
		
		break;
	}
	default:break;
	}
	return;
}

void rotate(float* eye, float* center, direction dir)
{
	const float alpha = 1.0f / (10 * PI);
	float x1, x2, y1, y2;
	x1 = eye[0], y1 = eye[2], x2 = center[0], y2 = center[2];
	switch (dir) {
	case left: {
		center[0] = x1 + (x2 - x1)*cos(alpha) + (y2 - y1)*sin(alpha);
		center[2] = y1 + (y2 - y1)*cos(alpha) - (x2 - x1)*sin(alpha);
		break;
	}
	case right: {
		center[0] = x1 + (x2 - x1)*cos(alpha) - (y2 - y1)*sin(alpha);
		center[2] = y1 + (y2 - y1)*cos(alpha) + (x2 - x1)*sin(alpha);
		break;
	}
	default:break;
	}
}


void outCollisionTest(float* eye, float* center, float x1,float x2,float z1, float z2)
{
	
	if (x1 < 0)x1 += 2;
	else x1 -= 2;

	if (x2 < 0)x2 += 2;
	else x2 -= 2;

	if (z1 < 0)z1 += 2;
	else z1 -= 2;

	if (z2 < 0)z2 += 2;
	else z2 -= 2;
	
	if (center[0] < x1) {
		center[0] = x1;
	}

	if (center[0] > x2) {
		center[0] = x2;
	}
	if (center[2] < z1) {
		center[2] = z1;
	}
	if (center[2] > z2) {
		center[2] = z2;
	}

	float distance = sqrt((eye[0] - center[0])*(eye[0] - center[0]) +
		(eye[2] - center[2])*(eye[2] - center[2]));

	if (distance <= 2.0f) {
		eye[0] = 2.0f*(eye[0] - center[0]) / distance + center[0];
		eye[2] = 2.0f*(eye[2] - center[2]) / distance + center[2];
	}
	bool flag = false;
	if (eye[0] < x1) {
		flag = true;
		eye[0] = x1;
	}
	if (eye[0] > x2) {
		flag = true;
		eye[0] = x2;
	}
	if (eye[2] < z1) {
		flag = true;
		eye[2] = z1;
	}
	if (eye[2] > z2) {
		flag = true;
		eye[2] = z2;
	}
	if (flag) {
		distance = sqrt((eye[0] - center[0])*(eye[0] - center[0]) +
			(eye[2] - center[2])*(eye[2] - center[2]));

		if (distance <=2.0f) {
			center[0] = 2.0f*(center[0] - eye[0]) / distance + eye[0];
			center[2] = 2.0f*(center[2] - eye[2]) / distance + eye[2];
		}
	}
	return;
}

void inCollisionTest(float* eye, float* center, float x1, float z1,
	float x2, float z2)
{

	//printf("%f,%f,%f,%f\n", center[0], center[2], eye[0], eye[2]);

	const float d = 2.0f;
	float _x1 = center[0], _x2 = eye[0], _z1 = center[2], _z2 = eye[2];
	float len = sqrt((_x2 - _x1)*(_x2 - _x1) + (_z2 - _z1)*(_z2 - _z1));
	
	dot d1(eye[0], eye[2]),d2(center[0],center[2]);
	dot d3(x1, z1), d4(x1, z2), d5(x2, z1), d6(x2, z2);

	if (SegmentIntersect(d1,d2,d4,d6)) {
		if (center[2] < eye[2]) {
			printf("1\n");
			center[0] = _x1 + (_x2 - _x1)*(z2 - _z1) / (_z2 - _z1);
			center[2] = z2;
			eye[0] = center[0] + d*(_x2 - _x1) / len;
			eye[2] = center[2] + d*(_z2 - _z1) / len;
		}
		else if (center[2] > eye[2]) {
			printf("2\n");
			eye[0] = _x2 + (_x1 - _x2)*(z2 - _z2) / (_z1 - _z2);
			eye[2] = z2;
			center[0] = eye[0] + d*(_x1 - _x2) / len;
			center[2] = eye[2] + d*(_z1 - _z2) / len;
		}
	}

	else if (SegmentIntersect(d1, d2, d5, d6)) {
		if (center[0]<eye[0]) {
			printf("3\n");
			center[0] = x2;
			center[2] = _z1 + (_z2 - _z1)*(x2 - _x1) / (_x2 - _x1);
			eye[0] = center[0] + d*(_x2 - _x1) / len;
			eye[2] = center[2] + d*(_z2 - _z1) / len;
		}
		else if (center[0]>eye[0]) {
			printf("4\n");
			eye[0] = x2;
			eye[2] = _z2 + (_z1 - _z2)*(x2 - _x2) / (_x1 - _x2);
			center[0] = eye[0] + d*(_x1 - _x2) / len;
			center[2] = eye[2] + d*(_z1 - _z2) / len;
		}
	}

	
	else if (SegmentIntersect(d1, d2, d3, d5)) {
		if (center[2] > eye[2]) {
			printf("5\n");
			center[0] = _x1 + (_x2 - _x1)*(z1 - _z1) / (_z2 - _z1);
			center[2] = z1;
			eye[0] = center[0] + d*(_x2 - _x1) / len;
			eye[2] = center[2] + d*(_z2 - _z1) / len;
		}
		else if (center[2] < eye[2]) {
			printf("6\n");
			eye[0] = _x2 + (_x1 - _x2)*(z1 - _z2) / (_z1 - _z2);
			eye[2] = z1;
			center[0] = eye[0] + d*(_x1 - _x2) / len;
			center[2] = eye[2] + d*(_z1 - _z2) / len;
		}
	}

	else if (SegmentIntersect(d1, d2, d3, d4)) {
		if (center[0] > eye[0]) {
			printf("7\n");
			center[0] = x1;
			center[2] = _z1 + (_z2 - _z1)*(x1 - _x1) / (_x2 - _x1);
			eye[0] = center[0] + d*(_x2 - _x1) / len;
			eye[2] = center[2] + d*(_z2 - _z1) / len;
		}
		else if (center[0] < eye[0]) {
			printf("8\n");
			eye[0] = x1;
			eye[2] = _z2 + (_z1 - _z2)*(x1 - _x2) / (_x1 - _x2);
			center[0] = eye[0] + d*(_x1 - _x2) / len;
			center[2] = eye[2] + d*(_z1 - _z2) / len;
		}
	}
	else if (_x1 - _x2 < 0.1f&&_x2 - _x1 >= -0.1f
		&& ((_z1>z1&&_z1<z2) || (_z2>z1&&_z2<z2))) {
		if (_x1 > x1&&_x1 < (x1 + x2) / 2) {
			
			center[0] = x1;
			eye[0] = x1;
		}
		else if (_x1 >(x1 + x2) / 2 && _x1 < x2) {
			
			center[0] = x2;
			eye[0] = x2;
		}
	}
	else if (_z1 - _z2 < 0.1f&&_z2 - _z1 >= -0.1f
		&& ((_x1>x1&&_x1<x2) || (_x2>x1&&_x2<x2))) {
		if (_z1 > z1&&_z1 < (z1 + z2) / 2) {
			
			center[2] = z1;
			eye[2] = z1;
		}
		else if (_z1 >(z1 + z2) / 2 && _z1 < z2) {
			
			center[2] = z2;
			eye[2] = z2;
		}
	}
}

void drawRect(GLuint texture)
{
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texture);  //选择纹理texture[status] 
	const GLfloat x1 = -0.5, x2 = 0.5;
	const GLfloat y1 = -0.5, y2 = 0.5;
	const GLfloat point[4][2] = { { x1,y1 },{ x1,y2 },{ x2,y2 },{ x2,y1 } };
	int dir[4][2] = { { 1,1 },{ 1,0 },{ 0,0 },{ 0,1 } };
	glBegin(GL_QUADS);

	for (int i = 0; i < 4; i++) {
		glTexCoord2iv(dir[i]);
		glVertex2fv(point[i]);
	}
	glEnd();
	glDisable(GL_TEXTURE_2D);
}

void drawCube(GLuint texture)
{
	glEnable(GL_TEXTURE_2D);
	int i, j;
	const GLfloat x1 = -0.5, x2 = 0.5;
	const GLfloat y1 = -0.5, y2 = 0.5;
	const GLfloat z1 = -0.5, z2 = 0.5;

	//指定六个面的四个顶点,每个顶点用3个坐标值表示   
	//前 后 上 下 左 右

	GLfloat point[6][4][3] = {
		{ { x1,y1,z1 },{ x1,y2,z1 },{ x2,y2,z1 },{ x2,y1,z1 } },
		{ { x1,y1,z2 },{ x2,y1,z2 },{ x2,y2,z2 },{ x1,y2,z2 } },
		{ { x1,y2,z1 },{ x1,y2,z2 },{ x2,y2,z2 },{ x2,y2,z1 } },
		{ { x1,y1,z1 },{ x2,y1,z1 },{ x2,y1,z2 },{ x1,y1,z2 } },
		{ { x2,y1,z1 },{ x2,y2,z1 },{ x2,y2,z2 },{ x2,y1,z2 } },
		{ { x1,y1,z1 },{ x1,y1,z2 },{ x1,y2,z2 },{ x1,y2,z1 } },
	};

	int dir[6][4][2] = {
		{ { 0,0 },{ 0,1 },{ 1,1 },{ 1,0 } },
		{ { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } },
		{ { 0,1 },{ 0,0 },{ 1,0 },{ 1,1 } },
		{ { 1,1 },{ 0,1 },{ 0,0 },{ 1,0 } },
		{ { 1,0 },{ 1,1 },{ 0,1 },{ 0,0 } },
		{ { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } },
	};


	for (i = 0; i < 6; i++) {
	   glBindTexture(GL_TEXTURE_2D, texture);
		glBegin(GL_QUADS);
		for (j = 0; j < 4; j++) {
			glTexCoord2iv(dir[i][j]);
			glVertex3fv(point[i][j]);
		}
		glEnd();
	}
	glDisable(GL_TEXTURE_2D);
}

main.cpp

// glutEx1.cpp : 定义控制台应用程序的入口点。
//
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include"test.h"

#include<io.h>
#include <math.h>       /* for cos(), sin(), and sqrt() */  


#define roomSizeX 100
#define roomSizeY 15
#define roomSizeZ 100

GLuint texture[3];
//视区
float whRatio;
int wHeight = 0;
int wWidth = 0;


//视点
float center[] = { 0, 0, 35 };
float eye[] = { 0, 0, 40 };


void drawScene()
{
	//地板
	glPushMatrix();
	glTranslatef(0.0f, -1.0f*roomSizeY / 2.0f, 0.0f);
	glRotatef(90, 1, 0, 0);

	glScalef(roomSizeX, roomSizeZ, 1);
	drawRect(texture[0]);
	glPopMatrix();

	//天花板	
	glPushMatrix();
	glTranslatef(0.0f, 1.0f*roomSizeY / 2.0f, 0.0f);
	glRotatef(270, 1, 0, 0);

	glScalef(roomSizeX, roomSizeZ, 1);
	drawRect(texture[0]);
	glPopMatrix();

	//墙壁(前)
	glPushMatrix();
	glTranslatef(0.0f, 0.0f, -1.0f*roomSizeZ / 2.0);
	glRotatef(180, 1, 0, 0);
	glRotatef(180, 0, 0, 1);

	glScalef(roomSizeX, roomSizeY, 1);
	drawRect(texture[1]);
	glPopMatrix();

	//墙壁(后)
	glPushMatrix();
	glTranslatef(0.0f, 0.0f, 1.0f*roomSizeZ / 2.0f);
	glScalef(roomSizeX, roomSizeY, 1);

	drawRect(texture[1]);
	glPopMatrix();

	//墙壁(左)
	glPushMatrix();
	glTranslatef(-1.0f*roomSizeX / 2.0f, 0.0f, 0.0f);
	glRotatef(270, 0, 1, 0);

	glScalef(roomSizeZ, roomSizeY, 1);
	drawRect(texture[1]);
	glPopMatrix();

	//墙壁(右)
	glPushMatrix();
	glTranslatef(1.0f*roomSizeX / 2.0f, 0.0f, 0.0f);
	glRotatef(90, 0, 1, 0);

	glScalef(roomSizeZ, roomSizeY, 1);
	drawRect(texture[1]);
	glPopMatrix();

	//中间墙壁
	glPushMatrix();
	glScalef(50, 15, 50);
	drawCube(texture[1]);
	glPopMatrix();

	//箱子
	glPushMatrix();
	glTranslatef(-1.0f*roomSizeX / 2.0f+2.5f, -1.0f*roomSizeY / 2.0f + 2.5f, -1.0f*roomSizeZ / 2.0f + 5.0f);
	glScalef(5, 5, 5);
	drawCube(texture[2]);
	glPopMatrix();

	glPushMatrix();
	glTranslatef(-1.0f*roomSizeX / 2.0f + 2.5f, -1.0f*roomSizeY / 2.0f + 7.5f, -1.0f*roomSizeZ / 2.0f + 5.0f);
	glScalef(5, 5, 5);
	drawCube(texture[2]);
	glPopMatrix();

	glPushMatrix();
	glTranslatef(-1.0f*roomSizeX / 2.0f + 7.5f, -1.0f*roomSizeY / 2.0f + 2.5f, -1.0f*roomSizeZ / 2.0f + 5.0f);
	glScalef(5, 5, 5);
	drawCube(texture[2]);
	glPopMatrix();


	glPushMatrix();
	glTranslatef(-1.0f*roomSizeX / 2.0f + 2.5f, -1.0f*roomSizeY / 2.0f +2.5f, -1.0f*roomSizeZ / 2.0f + 10.0f);
	glScalef(5, 5, 5);
	drawCube(texture[2]);
	glPopMatrix();
}

void updateView(int height, int width)
{
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影     
	glLoadIdentity();   //初始化矩阵为单位矩阵        
	whRatio = (GLfloat)width / (GLfloat)height;  //设置显示比例   
	gluPerspective(45.0f, whRatio, 1.0f, 150.0f); //透视投影      
												  //glFrustum(-3, 3, -3, 3, 3,100);    
	glMatrixMode(GL_MODELVIEW);  //设置矩阵模式为模型  
}

void reshape(int width, int height)
{
	if (height == 0)      //如果高度为0    
	{
		height = 1;   //让高度为1(避免出现分母为0的现象)    
	}

	wHeight = height;
	wWidth = width;

	updateView(wHeight, wWidth); //更新视角    
}


void idle()
{
	glutPostRedisplay();
}



void init()
{
	glEnable(GL_DEPTH_TEST);//开启深度测试      
	glEnable(GL_LIGHTING);  //开启光照模式 
	glGenTextures(3, texture);
	loadTex(0, "1.bmp", texture);
	loadTex(1, "6.bmp", texture);
	loadTex(2, "8.bmp", texture);


}


void key(unsigned char k, int x, int y)
{
	switch (k)
	{
	case 27:
	case 'q': {exit(0); break; } //退出

	case 'a': { //左移
		move(eye, center, left);
		break;
	}
	case 'd': { //右移
		move(eye, center, right);
		break;
	}
	case 'w': { //前移
		move(eye, center, front);
		break;
	}
	case 's': {  //后移
		move(eye, center, back);
		break;
	}

	case 'j': {//视角左移
		rotate(eye, center, left);
		break;
	}
	case 'l': {//视角右移
		rotate(eye, center, right);
		break;
	}
	case 'i': {//视角上移
		center[1] += 0.4f;
		break;
	}
	case 'k': {//视角上移
		center[1] -= 0.4f;
		break;
	}

	}
	//与墙壁的碰撞检测
	
	outCollisionTest(eye, center, -roomSizeX/2, roomSizeX / 2,-roomSizeZ/2, roomSizeZ / 2);
	inCollisionTest(eye, center, -30, -30, 30, 30);
	inCollisionTest(eye, center, -50, -45, -42, -37);
	inCollisionTest(eye, center, -45, -50, -37, -42);
	
	updateView(wHeight, wWidth); //更新视角
}

void redraw()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓存  
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();	//初始化矩阵为单位矩阵  	
	gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0);				// 场景(0,0,0)的视点中心 (0,5,50),Y轴向上
	glPolygonMode(GL_FRONT, GL_FILL);
    glFrontFace(GL_CCW);
    glEnable(GL_CULL_FACE);
    // 启用光照计算
    glEnable(GL_LIGHTING);
    // 指定环境光强度(RGBA)
    GLfloat ambientLight[] = { 2.0f, 2.0f, 2.0f, 1.0f };

    // 设置光照模型,将ambientLight所指定的RGBA强度值应用到环境光
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
    // 启用颜色追踪
    glEnable(GL_COLOR_MATERIAL);
    // 设置多边形正面的环境光和散射光材料属性,追踪glColor
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);


	drawScene();//绘制场景 
	glutSwapBuffers();//交换缓冲区
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);//对glut的初始化       
	glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
	//初始化显示模式:RGB颜色模型,深度测试,双缓冲         
	glutInitWindowSize(600, 600);//设置窗口大小       
	int windowHandle = glutCreateWindow("Simple GLUT App");//设置窗口标题     	
	glutDisplayFunc(redraw); //注册绘制回调函数       
	glutReshapeFunc(reshape);   //注册重绘回调函数       
	glutKeyboardFunc(key); //注册按键回调函数     
	glutIdleFunc(idle);//注册全局回调函数:空闲时调用     
 
	init();
	glutMainLoop();  // glut事件处理循环     
	return 0;
}


  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值