1. 程式人生 > >opengl學習筆記(三)——玩弄模型(使用者互動)

opengl學習筆記(三)——玩弄模型(使用者互動)

經過前面兩節的功夫,我們算是搞到了一個還算可以的模型,但我們想要把模型翻來翻去,移動一下還是不行的。並且,根據使用者(老師)需求,我們要讓模型能以線框,點的方式繪製。也就有了這最後一講。

首先,我們通過滑鼠左鍵來旋轉物體。這就需要在滑鼠回撥函式裡下功夫了

if (state == GLUT_DOWN && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動起始點
	{
		flag = 0;
		r_x1 = r_x2 = x;
		r_y1 = r_y2 = y;
        } 
if (state == GLUT_UP && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動最終點並將旋轉量儲存進d,e
    {
        d += (r_x2 - r_x1) / 3;
        e -= (r_y2 - r_y1) / 3;
        r_x1 = r_x2 = r_y1 = r_y2 = 0;
    }

這裡的d,e分別代表了繞著y和x軸旋轉的分量。為什麼沒有Z?因為我們可以用x,y的旋轉來代替繞Z的旋轉,這裡不具體說明了。

我們用同樣的方法設定中鍵繫結平移操作。

if (state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON)//記錄平移拖動起始點
	{
		flag = 1;
		mov_x1 = mov_x2 = x;
		mov_y1 = mov_y2 = y;

	}
if (state == GLUT_UP && button == GLUT_MIDDLE_BUTTON)//記錄平移最終點,並將平移量存入trans_x,trans_y
	{
		trans_x += mov_x2 - mov_x1;
		trans_y += mov_y1 - mov_y2;
		mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0;
	}  

同時,我們需要在滑鼠移動的回撥函式裡做更改來實現拖動。

void end(int x, int y)
{
	if (!flag)
	{
		r_x2 = x;
		r_y2 = y;
	}
	if (flag == 1)
	{
		mov_x2 = x;
		mov_y2 = y;
	}
}

我們增加flag來區別左鍵和中鍵的拖動。

最後寫一個簡單的選單實現繪製的切換。這裡不多說明,可以看原始碼裡的操作。同時原始碼裡增加了支援鍵盤改變視角和模型的函式。

#include<iostream>
#include<fstream>
#include<GL/glew.h>//使用glew庫使用VBO
#include <GL/glut.h>
#include<cmath>
#include<ctime>
#include<vector>
#include<string>
#define BUFFER_OFFSET(bytes) ((GLubyte*)NULL+bytes)
#define T 100
#pragma comment(lib, "glew32.lib")//加個glew使用VBO,嘖
using namespace std;
struct Vertex
{
	GLfloat  x, y, z, nx, ny, nz;
}ver;
struct Index
{
	GLuint a, b, c;
};
struct Index_Line
{
	GLuint a, b;
};
Vertex* v_arr;//點集
Index* index_arr;//面索引
Index_Line* index_line_arr;//線索引
int v_num, index_num;
double a, b, c;//相機位置
double d, e, f;//旋轉角度
GLuint VBO;//只有一個的孤獨VBO
GLuint EBO[2];//索引緩衝指標
GLfloat l_position[4] = { 0,0,0,1 };
GLfloat r_x1, r_y1, r_x2, r_y2;//滑鼠拖動引數
GLint tag = 2;//初始表示畫多邊形
GLfloat mov_x1, mov_y1, mov_x2, mov_y2;//平移變數
GLfloat trans_x, trans_y;
GLfloat T_x, T_y, T_z;
GLint flag;//0表示滑鼠左鍵按下,1表示中鍵按下
void init(void)
{
	T_x = T_y = T_z = 0;
	char ch[50];
	ifstream in("lizhenxiout-repaired.ply");
	bool www = in.fail();
	for (int i = 0; i < 3; i++)
		in.getline(ch, 50);
	in >> ch;
	in >> ch;
	in >> ch;
	v_num = atoi(ch);
	for (int i = 0; i < 7; i++)
		in.getline(ch, 50);
	in >> ch;
	in >> ch;
	in >> ch;
	index_num = atoi(ch);
	for (int i = 0; i < 2; i++)
		in.getline(ch, 50);
	in.getline(ch, 50);
	//愚蠢的頭部讀取完畢
	v_arr = new Vertex[v_num];
	index_arr = new Index[index_num];
	index_line_arr = new Index_Line[3 * index_num];
	for (int i = 0; i < v_num; i++)
	{
		in >> v_arr[i].x >> v_arr[i].y >> v_arr[i].z >> v_arr[i].nx >> v_arr[i].ny >> v_arr[i].nz;
		T_x += v_arr[i].x;
		T_y += v_arr[i].y;
		T_z += v_arr[i].z;
	}
	T_x /= v_num;
	T_y /= v_num;
	T_z /= v_num;
	for (int i = 0; i < index_num; i++)
	{
		in >> index_arr[i].a >> index_arr[i].a >> index_arr[i].b >> index_arr[i].c;
		index_line_arr[3 * i].a = index_line_arr[3 * i + 2].b = index_arr[i].a;
		index_line_arr[3 * i + 1].a = index_line_arr[3 * i].b = index_arr[i].b;
		index_line_arr[3 * i + 2].a = index_line_arr[3 * i + 1].b = index_arr[i].c;
	}
	//使用VBO載入資料
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, v_num*sizeof(Vertex), v_arr, GL_STATIC_DRAW);
	//建立EBO索引
	glGenBuffers(2, EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[0]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_num*sizeof(Index), index_arr, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[1]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * index_num*sizeof(Index_Line), index_line_arr, GL_STATIC_DRAW);
	//設定頂點和法線指標
	glVertexPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), BUFFER_OFFSET(0));
	glNormalPointer(GL_FLOAT, 6 * sizeof(GLfloat), BUFFER_OFFSET(3 * sizeof(GLfloat)));
	//開啟對應功能
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GLUT_MULTISAMPLE);
	glMatrixMode(GL_PROJECTION);//設定投影矩陣
	glLoadIdentity();
	gluPerspective(60, 1, 0.1, 100000);
	glClearColor(0, 0, 0, 1);
	a = 200;
	b = 0.0;
	c = 0.0;
	d = e = f = 0;
	trans_x = trans_y = 0;
	r_x1 = r_y1 = r_x2 = r_y2 = 0;
	mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0;
}
void timerFunc(int value)
{
	glutPostRedisplay();
	glutTimerFunc(10, timerFunc, 1);
}
void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glLightfv(GL_LIGHT0, GL_POSITION, l_position);
	gluLookAt(b, c, a,
		T_x, T_y, T_z,
		0, 1, 0);
	glTranslated(trans_x + (mov_x2 - mov_x1)+T_x, trans_y + (mov_y1 - mov_y2)+T_y, T_z);
	glRotatef(d + (r_x2 - r_x1) / 3, 0, 1, 0);
	glRotatef(e - (r_y2 - r_y1) / 3, 1, 0, 0);
	glRotatef(f, 0, 0, 1);
	glTranslated(-trans_x - (mov_x2 - mov_x1)-T_x, -trans_y - (mov_y1 - mov_y2)-T_y, -T_z);
	glTranslated(trans_x + (mov_x2 - mov_x1), trans_y + (mov_y1 - mov_y2), 0);
	if (tag == 2)
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[0]);
		glDrawElements(GL_TRIANGLES, 3 * index_num, GL_UNSIGNED_INT, 0);
	}
	if (tag == 1)
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[1]);
		glDrawElements(GL_LINES, 2 * 3 * index_num, GL_UNSIGNED_INT, 0);
	}
	if (tag == 0)
	{
		glDrawArrays(GL_POINTS, 0, v_num);
	}
	//測試用三角形
	//glBegin(GL_TRIANGLES);
	//glVertex3i(-100, 0, 0);
	//glVertex3i(100, 0, 0);
	//glVertex3i(0, 200, 0);
	//glEnd();
	glutSwapBuffers();
}
void s_input(int key, int x, int y)//方向鍵改變相機Z
{
	if (key == GLUT_KEY_DOWN)
		a += 10;
	if (key == GLUT_KEY_UP)
		a -= 10;
}
void input(unsigned char key, int x, int y)
{
	switch (key)//你還是可以通過鍵盤控制引數
	{
	case'w':c += 10; break;//相機x,y移動
	case's':c -= 10; break;
	case'a':b -= 10; break;
	case'd':b += 10; break;
	case'h':d -= 5; break;//三個旋轉度設定
	case'k':d += 5; break;
	case'u':e -= 5; break;
	case'j':e += 5; break;
	case'i':f += 5; break;
	case'y':f -= 5; break;
	default:
		break;
	}

}
void start(int button, int state, int x, int y)
{
	if (state == GLUT_DOWN && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動起始點
	{
		flag = 0;
		r_x1 = r_x2 = x;
		r_y1 = r_y2 = y;

	}
	if (state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON)//記錄旋轉拖動起始點
	{
		flag = 1;
		mov_x1 = mov_x2 = x;
		mov_y1 = mov_y2 = y;

	}
	if (state == GLUT_UP && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動最終點並將旋轉量儲存進d,e
	{
		d += (r_x2 - r_x1) / 3;
		e -= (r_y2 - r_y1) / 3;
		r_x1 = r_x2 = r_y1 = r_y2 = 0;
	}
	if (state == GLUT_UP && button == GLUT_MIDDLE_BUTTON)//記錄平移最終點,並將平移量存入trans_x,trans_y
	{
		trans_x += mov_x2 - mov_x1;
		trans_y += mov_y1 - mov_y2;
		mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0;
	}
}
void end(int x, int y)
{
	if (!flag)
	{
		r_x2 = x;
		r_y2 = y;
	}
	if (flag == 1)
	{
		mov_x2 = x;
		mov_y2 = y;
	}
}
void menu(int value)
{
	tag = value;
}
int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(500, 500);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	GLenum err = glewInit();//初始化glew
	if (GLEW_OK != err)
	{
		exit(0);
	}
	init();
	glutDisplayFunc(display);
	glutTimerFunc(10, timerFunc, 1);
	glutSpecialFunc(s_input);
	glutKeyboardFunc(input);
	glutMouseFunc(start);
	glutMotionFunc(end);
	int menu_id = glutCreateMenu(menu);
	glutAddMenuEntry("dot", 0);
	glutAddMenuEntry("line", 1);
	glutAddMenuEntry("polygon", 2);
	glutAttachMenu(GLUT_RIGHT_BUTTON);
	glutMainLoop();
	return 0;
}