OpenGL画自行车+菜单设置(附源码)

OpenGL大作业是画一个自行车,难度不高,但是代码量还是比较大的,所以放到博客上展示一下,不具备什么技术性。总体工作量还是很大的,如果帮到各位记得点赞留言,非常感谢。

前面是代码讲解,开发工具是Visual Studio,全部代码放在最后,配置好OpenGL后可以直接粘贴使用。

0、准备工作

0.1 导入与点的定义

我们首先各种include

之后定义pi。因为这个程序是我之前画的一个贪吃蛇程序改的,所以画布的定义通过定义格子的数量及大小确定的,这里没有改所以就放在这了。

//这里的include要配置一下不然找不到glut.h,可以参考其他博客
#include <GL/glut.h>
#include <iostream>
#include <stdlib.h>
#include <math.h>
using namespace std;

//这里进行了一些宏定义,pi为圆周率已经定义到double的极限了
#define pi 3.1415926535897932
//这里是定义画布的大小,按格子划分画布,一个格子50,一行20个格子
#define gridSize 50
#define gridnum 20

然后为了方便我定义了一个点类,虽然也可以用二维数组或者结构体,不过还是习惯了定义成class。为了方便,全部public,大作业而已要什么严谨,因为OpenGL主要都是float类型,所以这里也用的float而不是double,理论上高精度转低精度问题不大,但是不想管了。

class Point
{
public:
	float x;
	float y;
	Point(float x, float y) {
		this->x = x;
		this->y = y;
	}
};

定义一个点A(1,1)的时候可以直接写

Point A=Point(1,1);

0.2 绘画分析

首先对自行车进行分析:

可以看到,自行车中最基本的共性元素有:带有粗细的线、各种大小的圆环,除此之外就什么都没有了。所以我觉得首先应该先写两个函数(造两个轮子):

1、DrawCircular(Po、r)以Po为圆心,r为半径画一个圆。(画个红色的圆,再用白色圆遮住就能形成一个圆环)。

2、DrawLine(p1,p2,width)绘制一条从p1到p2,指定粗细的线段(矩形)。

此外,由于自行车在运动,会涉及到大量的旋转,所以我认为还需要

3、PointRotate(Po、Pp、Ang)得到Pp点绕Po点Ang度之后的点。这样的话,辐条随着轮轴转的时候可以很容易获得对应的端点位置。

那么思路就是先定义上述基本的元素绘制函数,然后组合使用,再绘制车架、车轮、车座、踏板,再把这些复杂元素按位置组合起来就行。为了方便按位置组合,我们可以把车轮轴心、脚踏轴心、座椅位置等等关键位置point记录在成员变量里,这样对其的时候直接用就方便很多,修改的时候也不用到处修改了。

1、Bike类的结构

那么按照上述思路开始构建我们的bike类,首先应该记录关键点位和初始旋转角度,然后再定义绘制一帧自行车的函数。当然,在这之中也会有一些辅助的函数帮我们画这个自行车。

1.0 成员变量

为了方便,我们需要先定义好这个自行车的特征点。车轮位置、车把位置、旋转过的角度等。

这几个数字是我试过的,有兴趣也可以试试别的参数,画一个更大、更小的。

其中要强调的是旋转过的角度roAngle在绘制车轮轮辐和脚踏的时候,初始位置设置为roAngle,再让roAngle随着时间不断增大或减小,就可以让相应部件转动起来。

每次的增量通过mode控制,如果是高速就增加的多一点,反之就少一点。

class Bike
{
public:
	bool Fill = true;					//定义是填充还是线框,true是填充
	double roAngle = 0;					//绘图的初始角度,逐渐改变roAngle可以让图像运动
	Point wheel1 = Point(300, 0);		//前轮中心的位置
	Point wheel2 = Point(-300, 0);		//后轮的中心位置
	Point Pedal = Point(0, 0);			//脚踏板的中心位置
	char mode = 'L';					//运动速度,L、M、H分别是低中高速
··· ···
};

1.1 画圆

形参:accuracy是控制圆的精度的。传入参数Point O是圆心坐标,float r是圆的半径。

我搜索发现OpenGL是没有什么画圆的函数的,所以找到了别人的代码,稍微修改了一下。原理就是利用for循环画多边形。

没有加控制颜色的内容,所以画圆之前控制选择的什么颜色就是什么颜色。

void DrawCircular(Point O, float r) {
		int accuracy = 72;
		glBegin(GL_POLYGON);
		for (int i = 0; i < accuracy; ++i)
			glVertex2f(O.x + r * cos(2 * pi / accuracy * i), O.y + r * sin(2 * pi / accuracy * i));
		glEnd();
		glFlush();
	}

1.2 旋转点

形参:实现的功能就是将点p绕点o旋转angle°,这里用的角度制,会比较方便使用。

可以大幅简化轮辐旋转带来的代码编写任务。

Point PointRotate(Point o, Point p, double angle) {
		angle = (angle / 180) * pi;
		float px = p.x - o.x;
		float py = p.y - o.y;
		float tempx = px * cos(angle) - py * sin(angle);
		float tempy = px * sin(angle) + py * cos(angle);
		return Point(o.x + tempx, o.y + tempy);
	}

1.3 绘制斜矩形

OpenGL提供的绘制矩形的函数只能是一个方向的,而自行车绘图里会有很多斜的矩形,所以定义了斜着绘制矩形的函数。

形参:把斜的长方形看成一条线段,那么线段的端点就是p1和p2,width是线宽的一半。

原理就是p1p点在选段上,距离p1点width,然后将这个p1p点绕p1点旋转±90°得到两个点。同样找到p2p点,绕p2点旋转±90°,就可以得到四个点ABCD,最后调用绘制多边形,输入这四个点就可以实现了。

但是最后会出现bug,某条长边的边会出现曲线,原因未知,所以我就用两个三角形进行拼接成一个斜矩形,最后看的效果还是一样的。

这段代码写的时候感觉还有点意思,有种做高中数学简单题目的感觉,就不多赘述了,大家可以试着理解一下这段代码。

void DrawLine(Point p1, Point p2, float width) {
		float lineLength = sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
		float x_p1p = p1.x + (p2.x - p1.x) * width / lineLength;
		float y_p1p = p1.y + (p2.y - p1.y) * width / lineLength;
		Point p1p = Point(x_p1p, y_p1p);
		float x_p2p = p2.x + (p1.x - p2.x) * width / lineLength;
		float y_p2p = p2.y + (p1.y - p2.y) * width / lineLength;
		Point p2p = Point(x_p2p, y_p2p);
		Point A = PointRotate(p1, p1p, 90);
		Point B = PointRotate(p1, p1p, -90);
		Point C = PointRotate(p2, p2p, -90);
		Point D = PointRotate(p2, p2p, 90);
		glBegin(GL_POLYGON);
		glVertex2f(D.x, D.y);
		glVertex2f(C.x, C.y);
		glVertex2f(B.x, B.y);
		glEnd();
		glBegin(GL_POLYGON);
		glVertex2f(A.x, A.y);
		glVertex2f(C.x, C.y);
		glVertex2f(B.x, B.y);
		glEnd();
	}

1.4 画车架

再观察一下车架,也就是绿色部分,实际上就是几条直线,我们把这些线画出来就行。

首先为了方便,把自行车的几个特征点记下来,车轮wheel1和wheel2,车座底端vertex,车座Seat,车头head,车把Handle,脚踏板旋转中心Pedal。

之后要做的就是调用 绘制斜长方形的函数DrawLine,连接这几个点就行了。

void DrawFrame() {
		Point Seat = Point(-150, 300);
		Point vertex = Point(-125, 250);
        //定义颜色
		glColor3f(0.0, 1.0, 0.0);
        //画线
		DrawLine(wheel2, Pedal, 10);
		DrawLine(wheel2, vertex, 10);
		DrawLine(Pedal, Seat, 10);
		DrawLine(Point(Seat.x - 50, Seat.y), Point(Seat.x + 100, Seat.y), 15);
		Point head(200, 250);
		Point Handle(170, 350);
		DrawLine(head, vertex, 10);
		DrawLine(head, Pedal, 10);
		DrawLine(Handle, wheel1, 10);
		DrawLine(Point(Handle.x + 10, Handle.y), Point(Handle.x - 70, Handle.y), 10);
		DrawCircular(wheel1, 20);
		DrawCircular(wheel2, 20);
		DrawCircular(Pedal, 20);
	}

1.5 画脚踏板

形参:设置脚踏板的旋转中心为Point O

圆环用画圆函数,画一个大圆,一个白色的小同心圆盖住即可,没什么意思。

脚踏杆用DrawLine函数。但是因为设计到旋转,所以这个脚踏杆的初始位置一端就是旋转中心O,另一端就是绕O旋转angle°,由于这个角度需要随时间变化,所以这个在类里设计了一个成员变量roAngle记录当前旋转到的角度就可以了。

void DrawPedal(Point O) {
		double angle = (roAngle - 60) * pi / 180;
		DrawCircular(O, 50);
		glColor3f(1.0, 1.0, 1.0);
		DrawCircular(O, 30);
		glColor3f(1.0f, 0.0, 0.0);
		double length = 120;
		Point p3 = Point(O.x + length * cos(angle), O.y + length * sin(angle));
		glColor3f(0.0f, 1.0, 0.0);
		DrawLine(O, p3, 10);
		DrawLine(Point(p3.x - 50, p3.y), Point(p3.x + 50, p3.y), 10);
		DrawCircular(p3, 10);

	}

1.6 画轮子

红色大圆盖上白色的小圆,就可以画出红色的轮胎。然后用循环绘制辐条就行,这里是间隔60°。

形参:轮子的中心Point O

	void DrawWheel(Point O) {
		glColor3f(1.0f, 0.0, 0.0);
		DrawCircular(O, 200);
		glColor3f(1.0, 1.0, 1.0);
		DrawCircular(O, 180);
		glColor3f(1.0, 0.0, 0.0);
		glBegin(GL_POLYGON);               
		float weidth = 10;
		float length = 200;
		Point p1 = Point(-1 * weidth + O.x, -1 * length + O.y);
		Point p2 = Point(weidth + O.x, -1 * length + O.y);
		Point p3 = Point(weidth + O.x, O.y);
		Point p4 = Point(-1 * weidth + O.x, O.y);
		double angle = roAngle;
		for (int i = 0; i < 6; i++) {
			Point p5 = PointRotate(O, p1, angle);
			Point p6 = PointRotate(O, p2, angle);
			Point p7 = PointRotate(O, p3, angle);
			Point p8 = PointRotate(O, p4, angle);
			glBegin(GL_POLYGON);
			glVertex2f(p5.x, p5.y);
			glVertex2f(p6.x, p6.y);
			glVertex2f(p7.x, p7.y);
			glVertex2f(p8.x, p8.y);
			glEnd();
			angle = angle + 60;
		}
	}

1.7 画自行车

画元素的函数都编完了,那剩下的就是调用就行了。开始前先clear清除一下画布。

由于有要求说可选填充或者线框,所以做一个判断,是否Fill,决定使用GL_FILL还是GL_LINE。其他的注意遮挡关系,先后画图就好。

最后glFlush刷新到画布上,清空缓存。准备结束。

    void BikeDisplay() {
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        //是否填充?
		if (Fill)
		{
			glPolygonMode(GL_FRONT, GL_FILL);
			glPolygonMode(GL_BACK, GL_FILL);
		}
		else {
			glPolygonMode(GL_FRONT, GL_LINE);
			glPolygonMode(GL_BACK, GL_LINE);
		}
		glFrontFace(GL_CW);
        //画轮子
		DrawWheel(wheel1);
		DrawWheel(wheel2);
        //画车座
		DrawPedal(Pedal);
        //画车架
		DrawFrame();
        //刷新到画布
		glFlush();
        //清除缓存
		glutSwapBuffers();
	}

1.8 旋转函数

很简单就是让旋转过的角度roAngle(bike类的公有变量,)增加就行,不过需要根据运行速度低中高,分别设置增量。直接用Switch分支结构就行。

    void Rotate() {
		switch (mode)
		{
		case 'L': {roAngle = roAngle - 5; break; }
		case 'M': {roAngle = roAngle - 10; break; }
		case 'H': {roAngle = roAngle - 25; break; }
		default:
			break;
		}
	}

至此,bike类我们就定义完了。我们实现的功能是:调用BikeDisplay函数就可以按照特征点位与初始角度roAngle绘制一帧的自行车。

所以后面我们只要能连续修改roAngle,并调用BikeDisplay,就能实现预期的动画效果,这个可以通过OpenGL中的定时器实现。

2、子菜单定义

菜单的定义就是先把每个选项赋予对应的数字编号,比如glutAddMenuEntry("低速", 2);然后多个选项组装成一个子菜单,再把多个子菜单组装成主菜单,即可实现菜单的定义。下面这些代码写在main函数中。

先定义子菜单1,速度菜单

glutCreateMenu(speed_menu);是调用speed_menu函数,

传入的参数就是glutAddMenuEntry("低速", 2);中的这个2。每个标签分别对应一个传入参数。

实现的效果就是,当你点击低速选项时,就会调用speed_menu函数,并传入参数2,也就是调用speed_menu(2)。

那么后面我们在speed_menu中定义,当传入参数为2时,减小roAngle的该变量就可以降低旋转速度,后续也同理

	int sub_menu1 = glutCreateMenu(speed_menu);
	glutAddMenuEntry("低速", 2);
	glutAddMenuEntry("中速", 3);
	glutAddMenuEntry("高速", 4);

定义子菜单2,线框、填充

	int sub_menu2 = glutCreateMenu(mode_menu);
	glutAddMenuEntry("填充", 5);
	glutAddMenuEntry("线框", 6);

定义主菜单,把前面定义的两个菜单塞进来,并且取个名字显示出来

	//定义主菜单,并将前面的两个子菜单填入
	glutCreateMenu(main_menu);
	glutAddSubMenu("速度", sub_menu1);
	glutAddSubMenu("模式", sub_menu2);

	//设置鼠标右键为开启菜单的开关
	glutAttachMenu(GLUT_RIGHT_BUTTON);

回调函数:根据你在菜单选择的内容,依据返回的对应数字进行操作。

bike.mode控制着旋转速度,是bike的公共成员变量。修改之后就可以改变旋转速度。

bike.fill控制绘制的时候是否填充,是的话就填充,不是的话就线框。

void main_menu(int id)
{
}

void speed_menu(int id) {
	switch (id)
	{
	case 2: bike.mode = 'L';
		break;
	case 3: bike.mode = 'M';
		break;
	case 4: bike.mode = 'H';
		break;
	}
}


void mode_menu(int id) {
	switch (id)
	{
	case 5:bike.Fill = true;
		break;
	case 6:bike.Fill = false;
		break;
	}
}

3、开始绘画

3.1 初始化

确定初始化画布的颜色、大小、窗口位置、显示范围。照抄就行。

void init() {
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glColor3f(1.0f, 0.0f, 0.0f);           	
	gluOrtho2D(0.0, (gridnum + 2) * gridSize / 2, 0.0, (gridnum + 2) * gridSize / 2);
}

3.2 改变窗口尺寸时的函数

照抄就行。

void changSize(GLint w, GLint h) {
	GLfloat ratio;
	GLfloat coordinatesize = 1000.0f;
	if ((w == 0) || (h == 0))
		return;
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	ratio = (GLfloat)w / (GLfloat)h;
	if (w < h)
		glOrtho(-coordinatesize, coordinatesize, -coordinatesize / ratio, coordinatesize / ratio, -coordinatesize, coordinatesize);
	else
		glOrtho(-coordinatesize * ratio, coordinatesize * ratio, -coordinatesize, coordinatesize, -coordinatesize, coordinatesize);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

3.3 初始化一个自行车对象

Bike bike;

3.4 显示函数

void MyDisplay() {
    //让自行车旋转一下
	bike.Rotate();
    //绘制自行车
	bike.BikeDisplay();
}

3.5 定时调用函数

负责100ms之后再次显示,里面嵌套一次定时函数,就可以实现一直运动的效果。

void TimerFunc(int value)
{
	glutPostRedisplay();
	glutTimerFunc(100, TimerFunc, 1);
}

3.6 主函数

依次调用前面定义好的函数就行,没说到的代码基本是OpenGL项目都要写的代码。

int main(int argc, char** argv) {
    //初始化设置
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);	
	glutInitWindowPosition(200, 50);				
	glutInitWindowSize(gridnum * gridSize, gridnum * gridSize);
	glutCreateWindow("Point");						
	init();											
	glutDisplayFunc(MyDisplay);	

    //向定时器中添加TimerFunc函数,循环调用TimerFunc函数			
	glutTimerFunc(100, TimerFunc, 1);
    //窗口大小改变时调用changSize函数
	glutReshapeFunc(changSize);	

    //组装菜单
	int sub_menu1 = glutCreateMenu(speed_menu);
	glutAddMenuEntry("低速", 2);
	glutAddMenuEntry("中速", 3);
	glutAddMenuEntry("高速", 4);
	int sub_menu2 = glutCreateMenu(mode_menu);
	glutAddMenuEntry("填充", 5);
	glutAddMenuEntry("线框", 6);
	glutCreateMenu(main_menu);
	glutAddSubMenu("速度", sub_menu1);
	glutAddSubMenu("模式", sub_menu2);
	glutAttachMenu(GLUT_RIGHT_BUTTON);

    //启动循环绘制
	glutMainLoop();
	return 0;
}

4、全部代码

//这里的include要配置一下不然找不到glut.h,可以参考其他博客
#include <GL/glut.h>
#include <iostream>
#include <stdlib.h>
#include <math.h>
using namespace std;

//这里进行了一些宏定义,pi为圆周率已经定义到double的极限了
#define pi 3.1415926535897932
//这里是定义画布的大小,按格子划分画布,一个格子50,一行20个格子
#define gridSize 50
#define gridnum 20

//定义一个点类,方便后续操作,二维数组也行,不过我习惯了定义成类
class Point
{
public:
	float x;
	float y;
	Point(float x, float y) {
		this->x = x;
		this->y = y;
	}
};

//定义画自行车的类
class Bike
{
public:
	bool Fill = true;					//定义是填充还是线框,true是填充
	double roAngle = 0;					//绘图的初始角度,逐渐改变roAngle可以让图像运动
	Point wheel1 = Point(300, 0);		//前轮中心的位置
	Point wheel2 = Point(-300, 0);		//后轮的中心位置
	Point Pedal = Point(0, 0);			//脚踏板的中心位置
	char mode = 'L';					//运动速度,L、M、H分别是低中高速


	//绘图函数,将整个自行车画出来
	void BikeDisplay() {
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		if (Fill)
		{
			glPolygonMode(GL_FRONT, GL_FILL);
			glPolygonMode(GL_BACK, GL_FILL);
		}
		else {
			glPolygonMode(GL_FRONT, GL_LINE);
			glPolygonMode(GL_BACK, GL_LINE);
		}
		glFrontFace(GL_CW);
		DrawWheel(wheel1);
		DrawWheel(wheel2);
		DrawPedal(Pedal);
		DrawFrame();
		glFlush();
		glutSwapBuffers();
	}

	//逐渐改变绘制图形的初始角度,让自行车轮及脚踏板旋转的函数
	void Rotate() {
		switch (mode)
		{
		case 'L': {roAngle = roAngle - 5; break; }
		case 'M': {roAngle = roAngle - 10; break; }
		case 'H': {roAngle = roAngle - 25; break; }
		default:
			break;
		}
	}

private:
	//定义一个画圆的函数,圆心点O,半径为r
	void DrawCircular(Point O, float r) {
		int accuracy = 72;
		glBegin(GL_POLYGON);
		for (int i = 0; i < accuracy; ++i)
			glVertex2f(O.x + r * cos(2 * pi / accuracy * i), O.y + r * sin(2 * pi / accuracy * i));
		glEnd();
		glFlush();
	}

	//将P点绕o点旋转angle度
	Point PointRotate(Point o, Point p, double angle) {
		angle = (angle / 180) * pi;
		float px = p.x - o.x;
		float py = p.y - o.y;
		float tempx = px * cos(angle) - py * sin(angle);
		float tempy = px * sin(angle) + py * cos(angle);
		return Point(o.x + tempx, o.y + tempy);
	}

	//画一条“线”,是一个斜着的矩形,短边中心点为p1和p2,长方形的宽度为width
	//直接绘制斜的矩形会出长边变曲线的问题,所以分成两个三角形画
	void DrawLine(Point p1, Point p2, float width) {
		float lineLength = sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
		float x_p1p = p1.x + (p2.x - p1.x) * width / lineLength;
		float y_p1p = p1.y + (p2.y - p1.y) * width / lineLength;
		Point p1p = Point(x_p1p, y_p1p);
		float x_p2p = p2.x + (p1.x - p2.x) * width / lineLength;
		float y_p2p = p2.y + (p1.y - p2.y) * width / lineLength;
		Point p2p = Point(x_p2p, y_p2p);

		Point A = PointRotate(p1, p1p, 90);
		Point B = PointRotate(p1, p1p, -90);
		Point C = PointRotate(p2, p2p, -90);
		Point D = PointRotate(p2, p2p, 90);
		glBegin(GL_POLYGON);
		glVertex2f(D.x, D.y);
		glVertex2f(C.x, C.y);
		glVertex2f(B.x, B.y);
		glEnd();
		glBegin(GL_POLYGON);
		glVertex2f(A.x, A.y);
		glVertex2f(C.x, C.y);
		glVertex2f(B.x, B.y);
		glEnd();

	}

	//利用定义好的DrawLine函数,绘制自行车的车架
	void DrawFrame() {
		Point Seat = Point(-150, 300);
		Point vertex = Point(-125, 250);
		glColor3f(0.0, 1.0, 0.0);
		DrawLine(wheel2, Pedal, 10);
		DrawLine(wheel2, vertex, 10);
		DrawLine(Pedal, Seat, 10);
		DrawLine(Point(Seat.x - 50, Seat.y), Point(Seat.x + 100, Seat.y), 15);
		Point head(200, 250);
		Point Handle(170, 350);
		DrawLine(head, vertex, 10);
		DrawLine(head, Pedal, 10);
		DrawLine(Handle, wheel1, 10);
		DrawLine(Point(Handle.x + 10, Handle.y), Point(Handle.x - 70, Handle.y), 10);
		DrawCircular(wheel1, 20);
		DrawCircular(wheel2, 20);
		DrawCircular(Pedal, 20);
	}

	//利用DrawCircular和DrawLine函数,绘制脚踏板及轴
	void DrawPedal(Point O) {
		double angle = (roAngle - 60) * pi / 180;
		DrawCircular(O, 50);
		glColor3f(1.0, 1.0, 1.0);
		DrawCircular(O, 30);
		glColor3f(1.0f, 0.0, 0.0);
		double length = 120;
		Point p3 = Point(O.x + length * cos(angle), O.y + length * sin(angle));
		glColor3f(0.0f, 1.0, 0.0);
		DrawLine(O, p3, 10);
		DrawLine(Point(p3.x - 50, p3.y), Point(p3.x + 50, p3.y), 10);
		DrawCircular(p3, 10);

	}

	//利用DrawCircular、PointRotate函数,绘制车轮
	void DrawWheel(Point O) {
		glColor3f(1.0f, 0.0, 0.0);
		DrawCircular(O, 200);
		glColor3f(1.0, 1.0, 1.0);
		DrawCircular(O, 180);
		glColor3f(1.0, 0.0, 0.0);
		glBegin(GL_POLYGON);               
		float weidth = 10;
		float length = 200;
		Point p1 = Point(-1 * weidth + O.x, -1 * length + O.y);
		Point p2 = Point(weidth + O.x, -1 * length + O.y);
		Point p3 = Point(weidth + O.x, O.y);
		Point p4 = Point(-1 * weidth + O.x, O.y);
		double angle = roAngle;
		for (int i = 0; i < 6; i++) {
			Point p5 = PointRotate(O, p1, angle);
			Point p6 = PointRotate(O, p2, angle);
			Point p7 = PointRotate(O, p3, angle);
			Point p8 = PointRotate(O, p4, angle);
			glBegin(GL_POLYGON);
			glVertex2f(p5.x, p5.y);
			glVertex2f(p6.x, p6.y);
			glVertex2f(p7.x, p7.y);
			glVertex2f(p8.x, p8.y);
			glEnd();
			angle = angle + 60;
		}
	}
};
//---------------------大工程(Bike类定义)结束------------------------------------------

//初始化函数
void init() {
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glColor3f(1.0f, 0.0f, 0.0f);           	
	gluOrtho2D(0.0, (gridnum + 2) * gridSize / 2, 0.0, (gridnum + 2) * gridSize / 2);
}

//改变尺寸时的函数
void changSize(GLint w, GLint h) {
	GLfloat ratio;
	GLfloat coordinatesize = 1000.0f;
	if ((w == 0) || (h == 0))
		return;
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	ratio = (GLfloat)w / (GLfloat)h;
	if (w < h)
		glOrtho(-coordinatesize, coordinatesize, -coordinatesize / ratio, coordinatesize / ratio, -coordinatesize, coordinatesize);
	else
		glOrtho(-coordinatesize * ratio, coordinatesize * ratio, -coordinatesize, coordinatesize, -coordinatesize, coordinatesize);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

//初始化全局变量bike
Bike bike;

//主函数的display回调函数,旋转车轮并让bike画图
void MyDisplay() {
	bike.Rotate();
	bike.BikeDisplay();
}

//设置定时的回调函数,调用MyDisplay,并再次定时完成循环绘图
void TimerFunc(int value)
{
	glutPostRedisplay();
	glutTimerFunc(100, TimerFunc, 1);
}

//主菜单的回调函数,主菜单没有独立子项,所以什么也不用做,不报错就行
void main_menu(int id)
{
}

//调节速度的子菜单,id:2,3,4分别代表低中高速度
void speed_menu(int id) {
	switch (id)
	{
	case 2: bike.mode = 'L';
		break;
	case 3: bike.mode = 'M';
		break;
	case 4: bike.mode = 'H';
		break;
	}
}


//调节显示模式的子菜单,id:5,6分别代表填充与线框
void mode_menu(int id) {
	switch (id)
	{
	case 5:bike.Fill = true;
		break;
	case 6:bike.Fill = false;
		break;
	}
}


int main(int argc, char** argv) {
	//常规的初始化、定义窗口大小及位置的步骤
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);	
	glutInitWindowPosition(200, 50);				
	glutInitWindowSize(gridnum * gridSize, gridnum * gridSize);
	glutCreateWindow("Point");						
	init();											
	glutDisplayFunc(MyDisplay);		
    
    //把前面写的TimerFunc函数添加到定时任务中,每100ms调用一次。				
	glutTimerFunc(100, TimerFunc, 1);

    //窗口大小改变的时候调用changSize函数
	glutReshapeFunc(changSize);	

	//定义速度子菜单及内容
	int sub_menu1 = glutCreateMenu(speed_menu);
	glutAddMenuEntry("低速", 2);
	glutAddMenuEntry("中速", 3);
	glutAddMenuEntry("高速", 4);

	//定义模式子菜单及内容
	int sub_menu2 = glutCreateMenu(mode_menu);
	glutAddMenuEntry("填充", 5);
	glutAddMenuEntry("线框", 6);

	//定义主菜单,并将前面的两个子菜单填入
	glutCreateMenu(main_menu);
	glutAddSubMenu("速度", sub_menu1);
	glutAddSubMenu("模式", sub_menu2);

	//设置鼠标右键为开启菜单的开关
	glutAttachMenu(GLUT_RIGHT_BUTTON);
	glutMainLoop();
	return 0;
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值