最近在用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
using namespace std;
template
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);
//取得顶点
stacktp;
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
#include
#include
using namespace std;
Convex c;
float PROJECT_MATRIX[16];
float LOOKAT_MATRIX[16];
/**
结构体steps用于记录模仿的拧动历史,便于还原模仿
*/
//根据矩阵mat,将o_position转换为n_position
void 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对象并存入steps
vectorsteps;
//魔方各个面的颜色,可随意更该
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.2
template
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(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 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序号,没有转动时一直为-1
int 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 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();
}
感谢阅览,若有什么好的建议,指教,想法,请留言,谢谢!