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

来源:互联网 发布:阿克苏怎样优化网站 编辑:程序博客网 时间:2024/05/18 07:28

最近在用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           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);

}

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

通过该代码主要了解到,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);//右乘M
glMultMatrixf(MATRIX);//右乘MATRIX
glGetFloatv(GL_MODELVIEW_MATRIX, ABSOLUTE_MATRIX);//获得当前矩阵,即ABSOLUTE_MATRIX
glPopMatrix();//恢复之前保存的矩阵


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

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:2float 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属于该groupvoid getMember() {int k = 0;for (int i = 0;i < 27;i++) {//当cneter与当前方块的currentposition相差小于0.1时,//说明该方块属于该groupif (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           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);}}};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的成员cubefor (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() {//初始化groupfor (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();}


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


1 0