魔方1.0版 课时2

课程目录:

课时1博客地址: http://blog.csdn.net/shuxieweilai/article/details/6587501  //给出设计魔方的大概思路
课时2博客地址: http://blog.csdn.net/shuxieweilai/article/details/6588164 //构造出一个魔方的模型,但不做任何处理
课时3博客地址: http://blog.csdn.net/shuxieweilai/article/details/6589543 //可实现魔方的放大缩小 以及整体旋转
课时4博客地址: http://blog.csdn.net/shuxieweilai/article/details/6592830 // 可以实现一个键的扭动魔方

本课程的代码以及演示程序可以到http://download.csdn.net/source/3423269 下载

本课时最终代码展示:

 

 

如要手工改变观察魔方的角度,可以到CCube类中的的CCube ::draw函数中被注释掉的glRotatef(45.0f, 10f, 0.0f);去掉注释,只需要改变里面的45度角的值,就可以看到魔方的其他面。

本节课的任务:

       初始化出一个魔方,构造出一个魔方的模型。

好了 现在开始正题,通过上一课你应该了解了魔方制作的基本框架了,现在开始实现的第一步,构造出一个魔方的模型出来。

         如果没有学过opengl的先看完nehe教程的前12个课程,包括编译环境的配置,nehe的课程中都有提到,本人直接采用了nehe的第12课时的原代码作为魔法的主程序:显示列表,但是本人并未用到显示列表,如果想将我的代码改成可以贴图的魔方,就应该运用显示列表了,不然的话你的程序会变成蜗牛的。你自己可以参考我提供下载本课程的代码,对nehe的代码进行删除,具体的就是删除显示列表的函数,以及drawglsence里面的代码,以及其他的一小部分的代码,我将主程序命名为mian.cpp。主程序里拷贝的代码不再说明,免得变得繁琐冗长,自觉的学习nehe的教程吧。

       首先你得再main.cpp构造出一个魔方的全局对象, CMagicCube magiccube 位于LRESULT CALLBACK WndProc(……)之上,然后在int InitGL(GLvoid)函数里添加下面的代码magiccube.InitMagicCube();用来初始化魔方。好了现在先把main.cpp放在一边不管了,现在需要到Face.h里面添加下面的代码,我将会做出一一解释

static CPoint points[8] = 
{
	{-0.5, -0.5, -0.5},	//0 l d f
	{-0.5, -0.5,  0.5},	//1 l d b
	{-0.5,  0.5, -0.5},	//2 l u f
	{-0.5,  0.5,  0.5},	//3 l u b
	{0.5, -0.5, -0.5},		//4 r d f
	{0.5, -0.5,  0.5},		//5 r d b
	{0.5,  0.5, -0.5},		//6 r u f
	{0.5,  0.5,  0.5}		//7 r u b
};

static int points_index[6][4] = 
{
	//opengl建模,D3D下应有不同
	{1,3,2,0},//left
	{4,6,7,5},//right
	{1,0,4,5},//down
	{7,6,2,3},//up
	{5,7,3,1},//front
	{0,2,6,4}//back
};

static CColor init_colors[6]=
{
	
	{1.0f, 1.0f, 1.0f},
	{1.0f, 0.0f, 0.0f},
	{0.0f, 1.0f, 0.0f},
	{0.0f, 0.0f, 1.0f},
	{0.0f, 1.0f, 1.0f},
	{1.0f, 1.0f, 0.0f}
};

首先看init_colors[6] 这个最简单了代表着初始化魔方的六种颜色。

points[8] 代表着一个立方体的8个顶点的坐标(当立方体的中心为(0,0,0) ) ;

index[6][4] 首先先将立方体的8个顶点用0到7的数字标号 注释中的l代表lefe u代表up 其他的也就不用我细说了吧。

好了 接着我们就开始初始化一个魔方,让魔方的获取各种信息。刚才我们说到magiccube.InitMagicCube ,

现在我们就跳到MagicCube.cpp的文件中,开始编写初始化函数。代码如下

void CMagicCube::InitMagicCube()
{
	for(int i = -1; i < 2; i++)
		for(int j = -1; j < 2; j++)
			for(int z = -1; z < 2; z++)
			{
				m_cube[i+1][j+1][z+1].SetPoint((float)i,(float)j,(float)z);//设置每个立方体的中心位置
				m_cube[i+1][j+1][z+1].InitCube();//初始化每个立方体
			}
}

上面的代码运用了三重循环使得初始化了每个对应的立方体,而每次循环下标都是从-1开始到1,这是为了方便魔方位于屏幕中央,使得魔方的中心立方体的中心与(0,0,0)重合,现在我们跳到CCube.cpp里看看是如何初始化的,上面用到了两个函数 SetPoint 和InitCube,代码分别如下:

void CCube :: SetPoint(float a, float b, float c)
{
	m_pt.m_x = a;
	m_pt.m_y = b;
	m_pt.m_z = c;
}

 

void CCube :: InitCube()
{
	for(int i = 0; i< 6; i++)
	{
		for( int j = 0; j < 4; j++)
		{
			m_face[i].GetPeak(j) = points[ points_index[i][j] ];//设置立方体的每个顶点坐标
		}
		m_face[i].GetColorindex()i;//设置初始时的颜色
	}

}

上面比较难理解的估计就是m_face[i].GetPeak(j) = points[ points_index[i][j] ]这个了,如果你不明白这句话的用意,好好的领会下上文中几个static变量的意义。希望能多通过自己的思考,如果还是不明白的话可以在博客中留言,我会替你详细阐述。

这里又调用了CFace里的GetPeak和GetColorindex不多说明了吧, 看名字就完全知道用意了吧。 OK到目前我们的初始化工作可以结束了,现在开始绘画出魔方。

 重新回到Main.cpp 中在DrawGLScene(GLvoid) 函数中加入magiccube.draw()的代码。表示绘画魔方。

好现在跳入CMagicCube的类当中,实现绘画。代码如下

void CMagicCube::draw()
{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清楚深度缓存和颜色缓存
		
		for(int i = -1; i < 2; i++)
			for(int j = -1; j < 2; j++)
				for(int z = -1; z < 2; z++)
				{
					m_cube[i+1][j+1][z+1].draw();
				}
}


ok上面的代码glclear清除深度缓存和颜色缓存,下面的代码很容易看懂吧,那么就继续跳入CCube类中的draw代码。

void CCube ::draw()
{	glLoadIdentity();//重置投影矩阵
	glTranslatef(0.0f, 0.0f, -10.0f);//j将坐标系移入深为10的地方。使得能够观看到整个魔方!
	//glRotatef(45.0f, 1.0f, 1.0f, 0.0f); //改变坐标系的角度,从而可以观察到魔方的其他面
	glTranslatef(m_pt.m_x*1.1, m_pt.m_y*1.1, m_pt.m_z*1.1);
	for(int i = 0; i < 6; i++)
	{
		m_face[i].draw(i);
	}
}

关于opengl的那些函数的意义我就不深入分析了,关键这句话glTranslatef(m_pt.m_x*1.1, m_pt.m_y*1.1, m_pt.m_z*1.1);得加以说明,如果没有乘以1.1倍的话句话的含义是将

坐标的原点移到当前绘制立方体的中心,那为啥又要乘以1.1倍呢,如果不这样做的话,会出现每个立方体之间没有间隔,使得相同的图片混为一起。下面的两张图片就是一个没有乘以1.1,一个do it。

 

乘了之后的:

好了 这里下面的代码也很简单了 最后我们来继续看 CFace里面的draw函数了。代码如下:

void CFace::draw(int index)
{
	
	glColor3f(init_colors[index].m_r,init_colors[index].m_g,init_colors[index].m_b);//设置当前颜色
	glBegin(GL_QUADS);//绘制矩形
		
		for (int i=0; i<4; i++)
		{	
			glVertex3f(m_peak[i].m_x, m_peak[i].m_y,m_peak[i].m_z);//循环找到相应的表面顶点坐标

		}
		glEnd();
}


貌似注释上我都写了 又没啥可讲的了,好了 有什么问题请博客留言吧,我会尽自己的努力给你解释。但有的可以百度的问题,希望你能先百度google之后再提问。

本篇又该结束了, 如想深入学习可以看的另一篇博客 魔方1.0版 第三课时. (明天将会更新)

 

ok 最后再来看看这里用到的一些c++ 知识

首先头文件里是不建议定义全局变量的。原因如下:

#include的意思,是指将被include 的.h文件加入到这个.c文件中,然后一起编译形成一个编译单元。比如a.c 和b.c都include com.h,而com.h里面定义一个部分int com; 那么在a.c生成的a.obj里面有一个com变量,而b.obj里面也有一个com变量。那么在把a.obj和b.obj连接成一个可以执行文件里,发现com在两个中间文件都有定义,那编译器肯定会报重复定义的错误

然而可以定义static变量,而且定义之后必须马上跟着初始化,比如上面的points【8】的数组,如果你在添加一句points【0】 = **;编译器将会报错。

 

在CFace类里面的两个获取函数 一个GetPeak 和一个GetColorindex,他们为啥要返回引用呢?

你可以在CCube类中找到这样两句话m_face[i].GetPeak(j) = points[ points_index[i][j] ;   m_face[i].GetColorindex() = i; 很明显他们返回的值需要被当做左值来使用,而如果返回变量的话将会返回的是一个临时变量,临时变量只能充当右值使用,所以需要返回引用。

 

 


 

 


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值