首页 > 学院 > 开发设计 > 正文

OpenGL中如何实现通过鼠标点击选取对象(正交投影)

2019-11-06 07:23:05
字体:
来源:转载
供稿:网友

最近在用OpenGL写一个魔方程序,考虑到需要选取其中的小方块,于是我研究了半天,发现了这个方法

这种方法暂时只能在正交投影下使用,不需要OpenGL自带的什么glpick之类的方法,说实话,我现在并不想学习那种方法

接下来,我要介绍方法的大概思路:

该方法的核心是对凸包算法的使用以及OpenGL里的矩阵运算()即仿射变换)但是凸包算法已经是一个经典算法,大家尝试自己解决,如果实在不行,我下面会发一下我的土包代码。

首先建立cube对象,cube指的是魔方之中每个小的立方体方块。每个cube对象都维护:

一个相对矩阵(MATRIX[16]),

一个绝对矩阵(ABSOLUTE_MATRIX[16]),

一个初始位置数组origion_position[3],固定不变

一个绝对当前位置数组cur_abs_position[3],可以改变,值由origon_position与MATRIX相乘得出

这组数据中实际上用到的只是z坐标值(因此尚有改进之处),用于判断深度,如果忽略深度条件,鼠标点击可能会选取到多个对象,但是一般来说我们想选的是我们可以看到的,即离我们最近的哪一个对象,因此需要进行深度比较

(其实还有一个相对的当前位置current_position[3],但是这与今天要讲的选取没有关系,所以姑且不多提),

八个顶点的初始位置origion_vertex[24],固定不变

八个顶点的当前位置urrent_vertex[24],可以改变,值由origon_vertex与ABSOLUTE_MATRIX相乘得出

这八组数据主要用到每组数据的x坐标与y坐标,相当于八个二维点,通过这八个点来求出他们的凸包,进而当鼠标点击窗口某个位置时用于判断在哪些凸包之内

因为实际上只需要16个信息,因此尚有改进之处

整个程序还要维护一个矩阵M[16],用于进行整个魔方的旋转与通过与MATRIX结合来求ABSOLUTE_MATRIX;

接下来下介绍各种矩阵的获取方法:

MATRIX的获取方法:

因为魔方有6个面可以先把每个面作为一个组,然后每次旋转魔方的一个面时,对各个组的成员进行重新分配,对每个小方块(cube)的MATRIX重新获取

代码如下:

void rotate(int right, int angle) {//当time为9时,说明旋转了90度,此刻才会形成完整的一步旋转for (int i = 0;i < 9;i++) {glPushMatrix();//每做一次小的旋转,都要对小方块的当前位置更新一下glLoadIdentity();//当coord为零时,说明group所在的面与x轴垂直,则绕x轴旋转//         为1时,                  y           y//           2                     z           zif (coord == 0)glRotatef(angle, right, 0, 0);else if (coord == 1)glRotatef(angle, 0, right, 0);else if (coord == 2)glRotatef(angle, 0, 0, right);glMultMatrixf(cubes[i]->MATRIX);glGetFloatv(GL_MODELVIEW_MATRIX, cubes[i]->MATRIX);glPopMatrix();//每次小的旋转都要改变当前位置//这样才可以正确画出方块cubes[i]->changePosition();mc->firstNine[i] = cubes[i]->index;cubes[i]->changeAbsData(M);}

这个不是今天的重点,详情请研究原代码及注释

通过该代码主要了解到,MATRIX的获取是对每个小方块进行如下操作(伪代码):

for(i=0:n)

第一步,保存当前矩阵: glpushMatrix();

第二步,当前矩阵变为单位阵: glloadIdentity();

第三步: 调用一系列变换函数;

第四步,右乘当前cube的MATRIX: glmultMatrix(cube[i]->MATRIX);

第五步,获取新的MATRIX: glGetFloat(GL_MODEL_MATRIX,cube[i]->MATRIX);

第六步:还原之前保存的矩阵: glPopMatrix();

M的获取方法(例如要对图像进行总体的旋转),与MAYTRIX的获取有些类似,可以采用如下代码,改代码时glMotionFunc的一个回掉函数:

void motion(int x, int y) {//变换一下坐标系y = 600 - y;float dx = -stx +x;float dy = -sty + y;glLoadIdentity();glRotatef(2, -dy, dx, 0);glMultMatrixf(M);glGetFloatv(GL_MODELVIEW_MATRIX, M);glutPostRedisplay();stx = x;sty = y;}

其中:

stx,sty是鼠标按下左键时的坐标(已经经过变换,现在以左下角原点),

x,y是鼠标拖动的坐标,以左上角为原点,需要变换使其以左下角为原点

dx,dy是鼠标拖动的位移,通过他的方向来确定物体的旋转方向

有时可能会出现旋转方向与鼠标拖动方向下给你返的情况,则把一下两行代码的正负号变换一下:

float dx = -stx +x;float dy = -sty + y;

改为:

float dx = stx -x;float dy = sty - y;

尤其注意,每当拖动一次鼠标之后,在函数最后要更新stx,sty的位置

ABSOLUTE_MATRIX的方法很简单,调用一下下列代码段就可以:

glPushMatrix();//保存当前矩阵glLoadIdentity();//是当前矩阵变为单位阵glMultMatrixf(M);//右乘MglMultMatrixf(MATRIX);//右乘MATRIXglGetFloatv(GL_MODELVIEW_MATRIX, ABSOLUTE_MATRIX);//获得当前矩阵,即ABSOLUTE_MATRIXglPopMatrix();//恢复之前保存的矩阵

获得了各种矩阵,就可以求各种绝对位置,相对位置,求取方法就是简单向量与矩阵相称的原理,采用一种简单的函数就可以轻而易举地实现,下面是一种函数:

void changeVector(float *MAT, float* o_v, float *n_v) {float tm[16];//矩阵转置for (int i = 0;i < 4;i++) {for (int j = 0;j < 4;j++) {tm[i * 4 + j] = MAT[j * 4 + i];// PRintf("%f ", tm[i * 4 + j]);}// printf("/n");}for (int i = 0;i < 3;i++) {n_v[i] = 0;for (int j = 0;j < 3;j++) {n_v[i] += tm[i * 4 + j] * o_v[j];}}}

函数中,MAT表示要乘的矩阵,o_v表示变换之前的向量,n_v表示变换之后的向量

这样,我们就可以当每次相改变一次图形各部分位置时(例如,我的模仿可以旋转)就可以通过一下步骤来对各个顶点的信息进行更改:

第一步:获取M

第二步:获取每个cube对象的MATRIX

第三步:获取每个cube对象的ABSLOOLUTE_MATRIX

第四步:调用changeVector方法获取新的current_vertex和cur_abs_position

第五步:改变各个cube对象所维护的凸包的信息

这样,这一次图像变换完成(以上五步记作Change)

接下来又是一个新周期

一个变换周期可以如下概括:

一、鼠标选中{

检查那个对象被选中

}

二、判断是否改变(即是否拖动鼠标或旋转魔方){

若改变,则调用Change;

否则,不调用Change,即跳过

}

三、画图

当然,这并不是绝对的严格遵守的三步,有可能会直接从第二步开始,但是只有通过鼠标才能表现出对对象的选取,详细请见程序

我写的这段程序,有一些扩展功能,当然,还有一些没有实现的功能,这只是实验产品,希望大家有兴趣的加以完善

#pragma once#ifndef CONVEX_ROC#define CONVEX_ROC#include<stack>using namespace std;template<class T>class Convex {private:	struct point2 {		int x, y;		double ATAN;		point2() {};		~point2() {}		point2(const point2&p) {			x = p.x;			y = p.y;			ATAN = p.ATAN;		}		point2& Operator=(const point2&p) {			x = p.x;			y = p.y;			ATAN = p.ATAN;			return *this;		}		point2(int _x, int _y) {			x = _x;			y = _y;		};		int &operator[](int i) {			if (i == 0)return x;			if (i == 1)return y;			return x;		};	};	//交换两个点的数据	void Swap(point2& p1, point2& p2) {		swap(p1.x, p2.x);		swap(p1.y, p2.y);		swap(p1.ATAN, p2.ATAN);	}	//检测是否比子树的值大,如果大于子树则交换	//比较顺序,先与左子树比较,再与右子树比较	//先按y值比较,再按x值比较	void  check_xchange(const int& a, point2*ps, const int & allPointNum) {		if (ps[a].y > ps[a * 2].y) {			Swap(ps[a], ps[a * 2]);		}		else if (ps[a].y == ps[a * 2].y) {			if (ps[a].x > ps[a * 2].x) {				Swap(ps[a], ps[a * 2]);			}		}		if (a * 2 + 1 <= allPointNum)			if (ps[a].y > ps[a * 2 + 1].y) {				Swap(ps[a], ps[a * 2 + 1]);			}			else if (ps[a].y == ps[a * 2 + 1].y) {				if (ps[a].x > ps[a * 2 + 1].x) {					Swap(ps[a], ps[a * 2 + 1]);				}			}	}	//使用堆排序算法,求出y最小的点.当有多个y最小的点时,再从中选取x最小的	void HEAPresort(point2*ps, const int& point_num) {		for (int i = point_num / 2;i > 0;i--) {			check_xchange(i, ps, point_num);		}	}	//获得每个点的极角,	//通过反三角函数acos来确定角的大小	//改进后通过函数的单调性来确定ATAN的大小	void getJiJiao(point2*points, const int& point_num) {		for (int i = 2;i <= point_num;i++) {			if (points[i].x == points[1].x) {				points[i].ATAN = 0;				continue;			}			double t = points[i].x - points[1].x;			//if (points[i].x != points[1].x)			points[i].ATAN = -(t) / pow((pow(t, 2) + pow(points[i].y - points[1].y, 2)), 0.5);			//else points[i].ATAN = PI / 2;		}	}	//按照极角的大小,有小到大排列,从第二个开始排	void MERGESORT(point2*points, const int &point_num) {		for (int j = point_num - 1;j > 0;j--)			for (int i = 2;i <= j;i++) {				if (points[i].ATAN > points[i + 1].ATAN) {					Swap(points[i], points[i + 1]);				}				else if (points[i].ATAN == points[i + 1].ATAN) {					if (points[i].y > points[i + 1].y) {						Swap(points[i], points[i + 1]);					}					else if (points[i].x > points[i + 1].x) {						Swap(points[i], points[i + 1]);					}				}			}	}	//当返回值小于0时 说明是向左转(即p3在p1->p2左面),等于零则三点共线	int LeftTurn_CHA(point2 &p1, point2 &p2, point2 &p3) {		return (p1.x - p2.x)*(p3.y - p2.y) - (p1.y - p2.y)*(p3.x - p2.x);	}	bool inPolygon(point2 &p, point2*ps, int p_size) {		if (p_size < 3)return 0;		else if (p_size == 3) {			return (LeftTurn_CHA(ps[0], ps[1], p)) <= 0 && (LeftTurn_CHA(ps[1], ps[2], p)) <= 0 && (LeftTurn_CHA(ps[2], ps[0], p)) <= 0;		}		else {			int t = LeftTurn_CHA(ps[0], ps[p_size / 2], p);			if (t == 0) {				return LeftTurn_CHA(ps[p_size / 2], ps[p_size / 2 + 1], p) <= 0;			}			else if (t > 0) {				return inPolygon(p, ps, p_size / 2 + 1);			}			else			{				point2 *tps = new point2[p_size - p_size / 2 + 1];				tps[0] = ps[0];				for (int i = 1;i < p_size - p_size / 2 + 1;i++)					tps[i] = (ps + (p_size / 2))[i - 1];				bool in = inPolygon(p, tps, p_size - p_size / 2 + 1);				delete[]tps;				return in;			}		}	}	point2*vertex = 0;//顶点数组	int level = 1;//精确级别,精确度越高级别越大,都是10的整数幂	int tp_size = 0;//顶点数目public:	Convex() {}	~Convex() {		if (vertex)			delete[]vertex;	}	//T为一维数组,可以用作二维数组	bool creatHull(T *data, const int& point_num, const int& lev) {		level = lev;		point2*points = new point2[point_num + 1];		for (int i = 1;i < point_num + 1;i++) {			points[i] = point2(data[i * 2 - 2] * level, data[i * 2 - 1] * level);		}		HEAPresort(points, point_num);		getJiJiao(points, point_num);		MERGESORT(points, point_num);		//取得顶点		stack<point2>tp;		tp.push(points[1]);		tp.push(points[2]);		for (int i = 3;i <= point_num;i++) {			point2 *p2 = &tp.top();			tp.pop();			point2 *p1 = &tp.top();			tp.pop();			int t;			while ((t = LeftTurn_CHA(*p1, *p2, points[i])) >= 0) {				if (tp.size() == 0) {					p2 = &points[i];					break;				}				p2 = p1;				p1 = &tp.top();				tp.pop();			}			tp.push(*p1);			tp.push(*p2);			tp.push(points[i]);		}		//将栈中的数据转移到数组中来		vertex = new point2[(tp_size = tp.size())];		for (int i = tp_size - 1;i >= 0;i--) {			vertex[i] = tp.top();			tp.pop();			//printf("TP:%d,%d %f/n", vertex[i].x, vertex[i].y, vertex[i].ATAN);		}		delete[]points;		return true;	}	bool testIn(const T *point) {		point2 p(point[0] * level, point[1] * level);		return inPolygon(p, vertex, tp_size);	}	//需要用户自行释放	T* getNewVertx()const {		T*v = new T[tp_size * 2];		for (int i = 0;i < tp_size;i++) {			v[i * 2] = (T)(vertex[i].x) / level;			v[i * 2 + 1] = (T)(vertex[i].y) / level;		}		return v;	}	int getVertexSize() const {		return tp_size;	}};#endif // !CONVEX_ROC#include<iostream>#include<vector>#include<gl/glut.h>using namespace std;Convex<float> c;float PROJECT_MATRIX[16];float LOOKAT_MATRIX[16];/**结构体steps用于记录模仿的拧动历史,便于还原模仿*///根据矩阵mat,将o_position转换为n_positionvoid setPosition(float*mat, float*o_position, float *n_position) {	float tm[16];	for (int i = 0;i < 4;i++) {		for (int j = 0;j < 4;j++) {			tm[i * 4 + j] = mat[j * 4 + i];			//printf("%f ", tm[i * 4 + j]);		}		//printf("/n");	}	//printf("/n");	for (int i = 0;i < 3;i++) {		n_position[i] = 0;		//printf("np[%d]=", i);		for (int j = 0;j < 3;j++) {			n_position[i] += tm[i * 4 + j] * o_position[j];			//	printf("%d*%d+", (int)tm[i * 4 + j], p[j]);		}		//	printf("  = %d/n", np[i]);	}	//cout << np[0] << " " << np[1] << " " << np[2] << endl;}struct step {	int drawPlane;//旋转的面的序号	int rotate_direction;//旋转的方向	//在控制台输出魔方的历史数据	void PrintOut() {		cout << "PLANE: " << drawPlane << " DER: " << rotate_direction;	}};//创建step向量,每当旋转一次魔方,都会产生一个新的step对象并存入stepsvector<step>steps;//魔方各个面的颜色,可随意更该float color[7][3] = {	1,1,0,	0,1,0,	1,0.5,0,	0,0,1,	1,0,1,	0,1,1,	0.5,0.5,0.5};//画小方块cube的一个面,在cube方法中调用,即可将一个小方格画出void drawPlane(int a) {	glColor3fv(color[a]);	glBegin(GL_QUADS);	glVertex3f(1, 1, 0);	glVertex3f(-1, 1, 0);	glVertex3f(-1, -1, 0);	glVertex3f(1, -1, 0);	glEnd();}float CM[16] = { 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 };float p[3] = { 1,1,1 };float np[3];int absulueP[3];float AM[16];float o_range[18] = {	1,1,0.5,	1,1,1.5,	0.5,1,1,	1.5,1,1,	1,0.5,1,	1,1.5,1};float c_range[18];//画一个小方块,边长为2,中心在原点void cube() {	glPushMatrix();	//glTranslatef(1, 1, 0);	for (int i = 0;i < 3;i++) {		glPushMatrix();		if (i == 0)			glRotatef(90, 1, 0, 0);		if (i == 1)			glRotatef(90, 0, 1, 0);		glTranslatef(0, 0, 1);		drawPlane(i * 2);		glTranslatef(0, 0, -2);		drawPlane(1 + 2 * i);		glPopMatrix();	}	glPopMatrix();}void bigcube() {	//glTranslatef(2, 2, 0);	glPushMatrix();	glScalef(0.2, 0.2, 0.2);	glRotatef(90, 0, 1, 0);	cube();	glPopMatrix();}//八个顶点,最初的位置float ori_poinVer[24] = {	0.5,0.5,0.5,	0.5,0.5,1.5,	0.5,1.5,1.5,	0.5,1.5,0.5,	1.5,0.5,0.5,	1.5,0.5,1.5,	1.5,1.5,1.5,	1.5,1.5,0.5,};//八个顶点当前位置float cur_poinVer[24];float *convexVertex;//图报的顶点int conVertNum;//凸包的顶点数//求出每个顶点的当前坐标void changeVertex() {	float ver[16];	for (int i = 0;i < 8;i++) {		setPosition(AM, ori_poinVer + i * 3, cur_poinVer + i * 3);		//得到二维坐标,去掉深度		ver[i * 2] = cur_poinVer[i * 3];		ver[i * 2 + 1] = cur_poinVer[i * 3 + 1];	}	//求八个顶点的凸包	c.creatHull(ver, 8, 1000);	conVertNum = c.getVertexSize();	convexVertex = c.getNewVertx();	for (int i = 0;i < conVertNum;i++) {		//printf("%f,%f/n", convexVertex[i * 2], convexVertex[i * 2 + 1]);	}	delete[]convexVertex;}void smallcube() {	glPushMatrix();	glScalef(0.1, 0.1, 0.1);	glTranslatef(2, 2, 2);	cube();	glPopMatrix();	setPosition(CM, p, np);}void changeCM() {	glPushMatrix();	glLoadIdentity();	glRotatef(90, 1, 0, 0);	glMultMatrixf(CM);	glGetFloatv(GL_MODELVIEW_MATRIX, CM);	glPopMatrix();}float M[16] = { 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 };void changeAM() {	glPushMatrix();	glLoadIdentity();	glMultMatrixf(M);	glMultMatrixf(CM);	glGetFloatv(GL_MODELVIEW_MATRIX, AM);	glPopMatrix();	//printf("M:/n");	for (int i = 0;i < 4;i++) {		for (int j = 0;j < 4;j++) {			//		printf("%f ", M[j * 4 + i]);		}		//	printf("/n");	}	//printf("CM:/n");	for (int i = 0;i < 4;i++) {		for (int j = 0;j < 4;j++) {			//		printf("%f ", CM[j * 4 + i]);		}		//	printf("/n");	}	//printf("AM:/n");	for (int i = 0;i < 4;i++) {		for (int j = 0;j < 4;j++) {			//		printf("%f ", AM[j * 4 + i]);		}		//	printf("/n");	}	setPosition(AM, p, np);	//	printf("np:/n");	for (int i = 0;i < 3;i++) {		//	printf("%f ", np[i]);	}	//	printf("/n");	//	printf("6面:/n");	for (int i = 0;i < 6;i++) {		setPosition(AM, o_range + i * 3, c_range + i * 3);		for (int j = 0;j < 3;j++) {			//		printf("%f ", (c_range+i*3)[j]);		}		//		printf("/n");	}	//	printf("/n");	//	printf("CONVEX:/n");}//检查是否被选中bool picked(int x, int y) {	float p[2];	p[0] = (float)x / 60 - 5;	p[1] = (float)y / 60 - 5;	printf("%f %f/nIn: %d/n", p[0], p[1]);	return c.testIn(p);}bool rotae = 1;#define CUBE_SCALE_TIME 0.96#define MAGIC_CUBE_SCALE_TIME 0.2template<class T>T whatangle(const T *a, const T *b, const  T *c) {	T t = 0;	for (int i = 0;i < 3;i++) {		t += (a[i] - b[i])*(c[i] - b[i]);	}	return t;}//改变向量,通过对应矩阵改变相应点的坐标,通法void changeVector(float *MAT, float* o_v, float *n_v) {	float tm[16];	//矩阵转置	for (int i = 0;i < 4;i++) {		for (int j = 0;j < 4;j++) {			tm[i * 4 + j] = MAT[j * 4 + i];			//	printf("%f ", tm[i * 4 + j]);		}		//	printf("/n");	}	//printf("/n");	for (int i = 0;i < 3;i++) {		n_v[i] = 0;		//printf("np[%d]=", i);		for (int j = 0;j < 3;j++) {			n_v[i] += tm[i * 4 + j] * o_v[j];			//		printf("%d*%d+", (int)tm[i * 4 + j], p[j]);		}		//	printf("  = %d/n", np[i]);	}}/*小方块,一共需要27个,算上中间的*/struct Cube{	//画一个小方块,边长为2,中心在原点	int validcolor[3] = { -1,-1,-1 };	int validColorNum;	void setValideColor() {		validColorNum = 0;		int a[3];		for (int i = 0;i < 6;i++) {			for (int j = 0;j < 3;j++)				a[j] = (int)origion_position[j];			switch (i)			{			case 0:				a[1] -= 1;				break;			case 1:				a[1] += 1;				break;			case 2:				a[0] += 1;				break;			case 3:				a[0] -= 1;				break;			case 4:				a[2] += 1;				break;			case 5:				a[2] -= 1;				break;			}			int p[3] = { 0,0,0 };			int d[3];			for (int j = 0;j < 3;j++) {				d[j] = (int)origion_position[j];			}			if (whatangle<int>(p, d, a) < 0) {				validcolor[validColorNum++] = i;			}		}	}	void drawcube() {		glPushMatrix();		//glTranslatef(1, 1, 0);		int k = 0;		int able = validcolor[k++];		for (int i = 0;i < 3;i++) {			glPushMatrix();			if (i == 0)				glRotatef(90, 1, 0, 0);			if (i == 1)				glRotatef(90, 0, 1, 0);			glTranslatef(0, 0, 1);			if (i * 2 == able) {				able = validcolor[k++];				drawPlane(i * 2);			}			else drawPlane(6);			//glFlush();			glTranslatef(0, 0, -2);			if (i * 2 + 1 == able) {				able = validcolor[k++];				drawPlane(1 + 2 * i);			}			else drawPlane(6);			//	glFlush();			glPopMatrix();		}		glPopMatrix();	}	bool ispicked = 0;;//是否被选中	int index;//序号	bool drew = 0;//是否已经画过	float origion_position[3];//原始位置	float current_position[3];//旋转之后的当前位置,相对位置	float cur_abs_position[3];//绝对位置,由绝对矩阵与原始位置相乘而得	 //原始顶点位置	float origion_vertex[24];	//当前顶点位置,绝对位置,由原始顶点位置与绝对矩阵相乘得出	float current_vertex[24];	Convex<float> c;//维护一个凸包,用于选择	//旋转之后的当前仿射变换矩阵,相对矩阵	float MATRIX[16] = { 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 };	//绝对矩阵,由相对矩阵与总体变化的矩阵相乘而得	float ABSOLUTE_MATRIX[16] = { 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 };	//改变8个顶点坐标,绝对改变	void changeVertix() {		for (int i = 0;i < 8;i++) {			changeVector(ABSOLUTE_MATRIX, origion_vertex + 3 * i, current_vertex + 3 * i);		}	}	//更新凸包	void refreshConvex() {		float v[16];		for (int i = 0;i < 8;i++) {			//setPosition(AM, ori_poinVer + i * 3, cur_poinVer + i * 3);			//得到二维坐标,去掉深度			v[i * 2] = current_vertex[i * 3];			v[i * 2 + 1] = current_vertex[i * 3 + 1];		}		//求八个顶点的凸包		c.creatHull(v, 8, 1000);	}	bool picked(const float x, const  float y) {		float p[2] = { x,y };		return c.testIn(p);	}	//改变当前的位置current_position,改变相对位置	void changePosition() {		changeVector(MATRIX, origion_position, current_position);	}	void changeAbsPosition() {		changeVector(ABSOLUTE_MATRIX, origion_position, cur_abs_position);	}	//画出小方块	void draw() {		changeAbsData(M);		if (!drew) {			glPushMatrix();			//右乘当前矩阵			glMultMatrixf(MATRIX);			glPushMatrix();			//平移小方块,使各个小方块按照序号放在相应的位置			glTranslatef(index % 3 * 2 - 2, index / 3 % 3 * 2 - 2, index / 9 % 3 * 2 - 2);			//缩小一下,使得各个方块之间有缝隙			glScalef(CUBE_SCALE_TIME, CUBE_SCALE_TIME, CUBE_SCALE_TIME);			drawcube();			glPopMatrix();			glPopMatrix();			drew = 1;			if (ispicked) {				glColor3f(1, 1, 1);				glLineWidth(3);				glPushMatrix();				glLoadIdentity();				glScalef(MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME);				//glMultMatrixf(M);				glTranslatef(0, 0, -5);				glPushMatrix();				glBegin(GL_LINE_LOOP);				for (int i = 0;i < 4;i++)					glVertex2f((current_vertex + 3 * i)[0], (current_vertex + 3 * i)[1]);				for (int i = 7;i >= 4;i--)					glVertex2f((current_vertex + 3 * i)[0], (current_vertex + 3 * i)[1]);				glEnd();				glBegin(GL_LINES);				glVertex2f((current_vertex)[0], (current_vertex)[1]);				glVertex2f((current_vertex + 9)[0], (current_vertex + 9)[1]);				glVertex2f((current_vertex + 3)[0], (current_vertex + 3)[1]);				glVertex2f((current_vertex + 15)[0], (current_vertex + 15)[1]);				glVertex2f((current_vertex + 6)[0], (current_vertex + 6)[1]);				glVertex2f((current_vertex + 18)[0], (current_vertex + 18)[1]);				glVertex2f((current_vertex + 12)[0], (current_vertex + 12)[1]);				glVertex2f((current_vertex + 21)[0], (current_vertex + 21)[1]);				glEnd();				glPopMatrix();				glPopMatrix();			}		}	}	void changeAbsData(float*M) {		glMatrixMode(GL_MODELVIEW);		glPushMatrix();		glLoadIdentity();	//	glMultMatrixf(PROJECT_MATRIX);		//glMultMatrixf(LOOKAT_MATRIX);		glMultMatrixf(M);		glMultMatrixf(MATRIX);		glGetFloatv(GL_MODELVIEW_MATRIX, ABSOLUTE_MATRIX);		glPopMatrix();		refreshConvex();		changeVertix();		changeAbsPosition();	}};float verts[24] = {	-1,-1,-1,	-1,1,-1,	-1,1,1,	-1,-1,1,	1,-1,-1,	1,1,-1,	1,1,1,	1,-1,1};//魔方struct MagicCube {	//每次画的时候,首先画的9个方块的序号,初始化如下	int firstNine[9] = { 0,1,2,3,4,5,6,7,8 };	//27的小方块	Cube cubes[27];	MagicCube() {		//初始化位置信息		for (int i = 0;i < 27;i++) {			cubes[i].index = i;			cubes[i].current_position[0] = cubes[i].origion_position[0] = i % 3 - 1;			cubes[i].current_position[1] = cubes[i].origion_position[1] = i / 3 % 3 - 1;			cubes[i].current_position[2] = cubes[i].origion_position[2] = i / 9 % 3 - 1;			for (int j = 0;j < 8;j++) {				cubes[i].current_vertex[j * 3] = cubes[i].origion_vertex[j * 3] = cubes[i].origion_position[0] * 2 + verts[j * 3] * CUBE_SCALE_TIME;				cubes[i].current_vertex[j * 3 + 1] = cubes[i].origion_vertex[j * 3 + 1] = cubes[i].origion_position[1] * 2 + verts[j * 3 + 1] * CUBE_SCALE_TIME;				cubes[i].current_vertex[j * 3 + 2] = cubes[i].origion_vertex[j * 3 + 2] = cubes[i].origion_position[2] * 2 + verts[j * 3 + 2] * CUBE_SCALE_TIME;			}			cubes[i].setValideColor();			//cubes[i].changeAbsData(M);		//	printf("%d :%d %d %d/n", i, cubes[i].current_position[0], cubes[i].current_position[1], cubes[i].current_position[2]);		}	}	//画出整个魔方	void draw() {		//令每个小方块变为未画的状态		for (int i = 0;i < 27;i++) {			cubes[i].drew = 0;		}		//先画前九个方块		for (int i = 0;i < 9;i++) {			glPushMatrix();			//glMultMatrixf(cubes[firstNine[i]].MATRIX);			cubes[firstNine[i]].draw();			glPopMatrix();		}		//再画后18个方块,因为不能确定那几个先画,因此用27次循环,		//若遇到之前已经画过的,因为drew为true所以直接跳过		for (int i = 0;i < 27;i++)		{			//cubes[i].drew = 1;			glPushMatrix();			//glMultMatrixf(cubes[i].MATRIX);			cubes[i].draw();			glPopMatrix();		}	}};#define POSITIVE 1#define NEGTIVE -1#define COORD_X 0#define COORD_Y 1;#define COORD_Z 2//每次旋转时,旋转了第几次,//当time为0时,没有旋转,//当time为9时,本次大的旋转结束,time归零//每次大的旋转包含9次小的旋转,//每次小的旋转会旋转10度,每次大的旋转旋转90度int time = 0;//方块的组.有9个成员,每次旋转一次魔方,成员有可能需要变struct group {	int coord;//坐标不为零的坐标轴x:0,y:1,z:2	float center;//中心的坐标,只有1与-1两种可能	Cube *cubes[9];//成员的指针数组	MagicCube *mc;//魔方对象的指针	group() {}	group(MagicCube*m_c, int cen, int coor) {		mc = m_c;		center = cen;		coord = coor;	}	//初始化成员,	//或者	//每当有任何一个group旋转一次,需要改变其他group的成员信息,调用该方法即可	//该方法的作用是获得或改变该group的成员对象,使得满足一定要求的cube属于该group	void getMember() {		int k = 0;		for (int i = 0;i < 27;i++) {			//当cneter与当前方块的currentposition相差小于0.1时,			//说明该方块属于该group			if (abs(center - mc->cubes[i].current_position[coord]) < 0.1) {				cubes[k] = &mc->cubes[i];				//	printf("%d ", cubes[k]->index);				mc->firstNine[k++] = i;			}		}		//printf("/n");	}	//对group进行一次小的旋转,连续调用9次则构成一次大的旋转	//连续调用的目的是增强动画效果	void rotate(int right, int angle) {		//当time为9时,说明旋转了90度,此刻才会形成完整的一步旋转		for (int i = 0;i < 9;i++) {			glPushMatrix();			//每做一次小的旋转,都要对小方块的当前位置更新一下			glLoadIdentity();			//当coord为零时,说明group所在的面与x轴垂直,则绕x轴旋转			//         为1时,                  y           y			//           2                     z           z			if (coord == 0)				glRotatef(angle, right, 0, 0);			else if (coord == 1)				glRotatef(angle, 0, right, 0);			else if (coord == 2)				glRotatef(angle, 0, 0, right);			glMultMatrixf(cubes[i]->MATRIX);			glGetFloatv(GL_MODELVIEW_MATRIX, cubes[i]->MATRIX);			glPopMatrix();			//每次小的旋转都要改变当前位置			//这样才可以正确画出方块			cubes[i]->changePosition();			mc->firstNine[i] = cubes[i]->index;			cubes[i]->changeAbsData(M);		}	}};MagicCube mc;group gc[6];bool rotaing = 0;//是否正在转动int plane_rotating = -1;//正在转动的group序号,没有转动时一直为-1int rotat_der = -1;//转动的方向,顺时针or逆时针int rotate_angle = 10;//每一次小旋转的角度,四种为10,修改该值的同时也要修改time的值//计时器,用于实现转动时的动画效果void timer(int);int planeToDraw = -1;struct plane {	bool ispicked = 0;	Cube* of;	int color;	float vertex[8];	float original_center_time[3];	float current_center_time[3];	float *cur_vertex = 0;	int hulVerNum = 0;	Convex <float>c;	plane() {}	~plane() {		if (cur_vertex) {			delete[]cur_vertex;			cur_vertex = 0;		};	}	plane(const plane&p) {		of = p.of;		color = p.color;		for (int i = 0;i < 3;i++) {			original_center_time[i] = p.original_center_time[i];		}		for (int i = 0;i < 8;i++) {			vertex[i] = p.vertex[i];		}		//changeVector(of->ABSOLUTE_MATRIX, vertex, new_vertex);				refrushHull();	}	void refrushCurrentCenter() {		changeVector(of->ABSOLUTE_MATRIX, original_center_time, current_center_time);	}	plane& operator =(const plane& p) {		of = p.of;		color = p.color;		for (int i = 0;i < 3;i++) {			original_center_time[i] = p.original_center_time[i];		}		//changeVector(of->ABSOLUTE_MATRIX, vertex, new_vertex);				refrushHull();		return *this;	}	plane(Cube* o, int c) {		of = o;		color = c;		for (int i = 0;i < 3;i++) {			original_center_time[i] = of->origion_position[i];		}		switch (c)		{		case 0:			original_center_time[1] -= 1;			break;		case 1:			original_center_time[1] += 1;			break;		case 2:			original_center_time[0] += 1;			break;		case 3:			original_center_time[0] -= 1;			break;		case 4:			original_center_time[2] += 1;			break;		case 5:			original_center_time[2] -= 1;			break;		}				refrushHull();		/**			case 0:				a[1] -= 1;				break;			case 1:				a[1] += 1;				break;			case 2:				a[0] += 1;				break;			case 3:				a[0] -= 1;				break;			case 4:				a[2] += 1;				break;			case 5:				a[2] -= 1;				*/	}	void refrushHull() {		refrushCurrentCenter();		switch (color) {		case 3:			for (int i = 0;i < 4;i++) {				vertex[i * 2] = of->current_vertex[i * 3];				vertex[i * 2 + 1] = of->current_vertex[i * 3 + 1];			}			break;		case 2:			for (int i = 0;i < 4;i++) {				vertex[i * 2] = of->current_vertex[(i + 4) * 3];				vertex[i * 2 + 1] = of->current_vertex[(i + 4) * 3 + 1];			}			break;		case 0:			for (int i = 0;i < 2;i++) {				vertex[0 + i] = of->current_vertex[i];				vertex[2 + i] = of->current_vertex[9 + i];				vertex[4 + i] = of->current_vertex[12 + i];				vertex[6 + i] = of->current_vertex[21 + i];			}			break;		case 1:			for (int i = 0;i < 2;i++) {				vertex[0 + i] = of->current_vertex[3 + i];				vertex[2 + i] = of->current_vertex[6 + i];				vertex[4 + i] = of->current_vertex[15 + i];				vertex[6 + i] = of->current_vertex[18 + i];			}			break;		case 4:			for (int i = 0;i < 2;i++) {				vertex[0 + i] = of->current_vertex[9 + i];				vertex[2 + i] = of->current_vertex[6 + i];				vertex[4 + i] = of->current_vertex[21 + i];				vertex[6 + i] = of->current_vertex[18 + i];			}			break;		case 5:			for (int i = 0;i < 2;i++) {				vertex[0 + i] = of->current_vertex[3 + i];				vertex[2 + i] = of->current_vertex[12 + i];				vertex[4 + i] = of->current_vertex[15 + i];				vertex[6 + i] = of->current_vertex[i];			}			break;		}		c.creatHull(vertex, 4, 1000);		if (cur_vertex) {			delete[]cur_vertex;			//cur_vertex = 0;		}		cur_vertex = c.getNewVertx();		hulVerNum = c.getVertexSize();	}	bool picked(float x, float y) {		refrushHull();		bool t;		float p[2] = { x,y };		return t = c.testIn(p);	}	void drawLine() {		glPushMatrix();		glScalef(MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME);		glTranslatef(0, 0, -5);		//glMultMatrixf(M);	//	glMultMatrixf(of->MATRIX);		glLineWidth(10);		glColor3f(1, 0, 0);		glBegin(GL_LINE_LOOP);		for (int i = 0;i < hulVerNum;i++)			glVertex2fv(cur_vertex + 2 * i);		glEnd();		glPopMatrix();	}};plane planes[54];void display() {	for (int i = 0;i < 54;i++) {		planes[i].refrushHull();	}	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	glEnable(GL_DEPTH_TEST);	//glEnable(GL_POLYGON_SMOOTH);	glLoadIdentity();	//glScalef(30, 1, 1, 0);	glScalef(MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME);	//gluLookAt(0,  0,30, 0, 0, 0, 0, 1, 0);	glPushMatrix();	glMultMatrixf(M);	/*	//glPolygonMode()	//bigcube();	glPopMatrix();	glMultMatrixf(M);	glPushMatrix();	glMultMatrixf(CM);	smallcube();	*/	mc.draw();	glPopMatrix();	glPushMatrix();	glLoadIdentity();	if (planeToDraw >= 0) {		//for (int i = 0;i < 54;i++) {{		planes[planeToDraw].drawLine();	}	glPopMatrix();	//}	glFlush();	//changeAM();}float stx, sty;void mouse(int b, int s, int x, int y) {	if (b == GLUT_LEFT_BUTTON&&s == GLUT_DOWN) {		stx = x;		sty = 600 - y;	}	if (b == GLUT_RIGHT_BUTTON&&s == GLUT_DOWN) {		y = 600 - y;		float X = x, Y = y;		X = (X - 300) / 300 / MAGIC_CUBE_SCALE_TIME;		Y = (Y - 300) / 300 / MAGIC_CUBE_SCALE_TIME;		//printf("/n%f %f : ", X, Y);		int theNrearest;		bool first = 1;		for (int i = 0;i < 27;i++) {			mc.cubes[i].ispicked = 0;			if (mc.cubes[i].picked(X, Y)) {				//planes[i].refrushHull();			//	printf("%d/n", i);				if (first) {					mc.cubes[i].ispicked = 1;					theNrearest = i;					first = 0;				}				else {					if (mc.cubes[i].cur_abs_position[2] < mc.cubes[theNrearest].cur_abs_position[2]) {						mc.cubes[i].ispicked = 1;						mc.cubes[theNrearest].ispicked = 0;						theNrearest = i;					};				}			}			else mc.cubes[i].ispicked = 0;		}		bool firstPlane = 1;		planeToDraw = -1;		for (int i = 0;i < 54;i++) {			//planes[i].refrushHull();			if (planes[i].picked(X, Y)) {				if (firstPlane) {					planeToDraw = i;					firstPlane = 0;				}				else {					if (planes[i].picked(X, Y) && planes[i].current_center_time[2] < planes[planeToDraw].current_center_time[2]) {						planeToDraw = i;					};				}			}		}		glutPostRedisplay();		//printf("/n");	}}void getPlanes() {	int k = 0;	for (int i = 0;i < 27;i++) {		for (int j = 0;j < mc.cubes[i].validColorNum;j++) {			int t = mc.cubes[i].validcolor[j];			switch (t) {			case 3:				planes[k++] = plane(&mc.cubes[i], mc.cubes[i].validcolor[j]);				break;			case 2:				planes[k++] = plane(&mc.cubes[i], mc.cubes[i].validcolor[j]);				break;			case 4:				planes[k++] = plane(&mc.cubes[i], mc.cubes[i].validcolor[j]);				break;			case 5:				planes[k++] = plane(&mc.cubes[i], mc.cubes[i].validcolor[j]);				break;			case 0:				planes[k++] = plane(&mc.cubes[i], mc.cubes[i].validcolor[j]);				break;			case 1:				planes[k++] = plane(&mc.cubes[i], mc.cubes[i].validcolor[j]);				break;			}		}	}}void motion(int x, int y) {	//变换一下坐标系	y = 600 - y;	float dx = stx -x;	float dy = sty - y;	glLoadIdentity();	glRotatef(2, -dy, dx, 0);	glMultMatrixf(M);	glGetFloatv(GL_MODELVIEW_MATRIX, M);	glutPostRedisplay();	stx = x;	sty = y;}int cotl = 0;//一次大的旋转,输入group的序号,与旋转的方向即可void bigRotate(int groupIndex, int degree_direction) {	plane_rotating = groupIndex;	rotat_der = degree_direction;	glutTimerFunc(20, timer, 0);}int stepIndex;int step_index;bool recover_started = 0;void recover_timer(int t) {	if (!recover_started) {		recover_started = 1;		step_index = steps.size();	}	if (step_index > 0) {		step_index--;		bigRotate(steps[step_index].drawPlane, -steps[step_index].rotate_direction);		glutTimerFunc(1000, recover_timer, 0);	}	else {		steps.clear();		recover_started = 0;	}}void key(int k, int x, int y) {	y = 600 - y;	if (plane_rotating == -1)		switch (k) {		case GLUT_KEY_F4:			glutTimerFunc(200,recover_timer, 0);			return;		case GLUT_KEY_UP:		{			step s;			s.drawPlane = 0;			s.rotate_direction = -1;			steps.push_back(s);		}			bigRotate(0, -1);			break;		case GLUT_KEY_DOWN:			//printf("%d/n", picked(x, y));			break;		case GLUT_KEY_LEFT:			bigRotate(1, -1);			{				step s;				s.drawPlane = 1;				s.rotate_direction = -1;				steps.push_back(s);			}			break;		case GLUT_KEY_RIGHT:			bigRotate(1, 1);			{				step s;				s.drawPlane = 1;				s.rotate_direction = 1;				steps.push_back(s);			}			break;		case GLUT_KEY_F1:			bigRotate(2, -1);			break;		case GLUT_KEY_F2:			bigRotate(2, 1);			break;		}	//glutPostRedisplay();}void timer(int n) {	time++;//小旋转次数+1,加到9归零		   //调用小旋转	gc[plane_rotating].rotate(rotat_der, rotate_angle);	glutPostRedisplay();	//当time为9时,表示已经完成一次大的旋转,这停止旋转,而且要time归零	//而且要调整旋转之后各个group的成员改变	if (time == 9) {		plane_rotating = -1;		time = 0;		//改变各个group的成员cube		for (int i = 0;i < 6;i++) {			gc[i].getMember();		}		//不再注册计时器,即停止旋转,直接返回		return;	}	glutTimerFunc(50, timer, 0);}void passMotion(int x, int y) {	y = 600 - y;}void main() {	//初始化group	for (int i = 0;i < 6;i++) {		gc[i].mc = &mc;		gc[i].coord = i % 3;		if (i < 3)gc[i].center = -1;		else gc[i].center = 1;		gc[i].getMember();	}	getPlanes();	glutInitWindowSize(600, 600);	glutCreateWindow("TESTCUBE");	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);	glClearColor(0, 0, 0, 1);	glutDisplayFunc(display);	glutMouseFunc(mouse);	glutMotionFunc(motion);	//glutPassiveMotionFunc(passMotion);	glutSpecialFunc(key);	glMatrixMode(GL_PROJECTION);	glLoadIdentity();	glGetFloatv(GL_PROJECTION_MATRIX, PROJECT_MATRIX);//	glOrtho(-1, 1, -1, 1, 100, -100);	//gluPerspective(30, 1, 1, 100);		glMatrixMode(GL_MODELVIEW);	glLoadIdentity();	//gluLookAt(0, 0, 30, 0, 0, 0, 0, 1, 0);	glGetFloatv(GL_MODELVIEW_MATRIX, LOOKAT_MATRIX);	//glLoadIdentity();	//glScalef(MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME, MAGIC_CUBE_SCALE_TIME);	//glGetFloatv(GL_MODELVIEW_MATRIX, M);	glutMainLoop();}

感谢阅览,若有什么好的建议,指教,想法,请留言,谢谢!


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表