上一讲我们说了一条线,一直螺旋这延伸的线,那么这一讲我们的内容依旧在线上操作,不过今天的线要简单一些,我们假设有一条曲线如下:
//==========================
{
x = r*cos(w*t);
y = r*sin(w*t);
z = 0;
}
//==========================
这条线曲线大家一看就知道一个在xoy平面上的一条半圆弧,知道这个数学公式后要画出这么条曲线来是很简单的事,等下会说,不过我们的问题是在这条曲线上做文章,其实也没打算做什么,就是想弄个球出来而已,好吧,既然我们都已经画的出一条圆弧曲线,画球面不是更加简单吗?我们可以这么来说,求其实也就是圆弧旋转360°所形成的画面,ok,到这里,我们就可以写出球的参数方程来了:
//============================
{
x = r*cos(w*t)*cos(p*t);
y = r*sin(w*t);
z = r*cos(w*t)*sin(p*t);
}
//============================
特别说明一下,根据旋转轴的不同,上面的两个方程会有所不同,但根据对称性,结果会都是一样的。
ok,知道上面两个公式,我们就可以编写我们的程序了:
//==============================
class RoundData:public MDataBase{
public:
RoundData(){
m_rect_num = 0;
m_radio = 1;
m_step = PI/24.0;
m_xrot = 0.0;
m_yrot = 0.0;
MParticle::genRand(0.0, 1.0);
}
void update(){
repaint();
}
//这里就是我们所说的半圆弧曲线
void geneLine(){
m_line_datas.clear();
for(int i=0;
i<360;
++i){
MVector3F next_point;
next_point.m_x = m_radio*std::cos(0.5*PI-i*m_step);
next_point.m_y = m_radio*std::sin(0.5*PI-i*m_step);
next_point.m_z = 0.0;
m_line_datas.push_back(next_point);
}
}
//通过上面的曲线我们就可以得出球的数据
void geneRound(){
if(m_line_datas.empty())
geneLine();
m_round_data.clear();
m_round_data.push_back(m_line_datas);
std::vector<MVector3F>line_data = m_round_data.at(m_round_data.size()-1);
for(int i=0;
i<48;
++i){
std::vector<MVector3F>next_line;
next_line.clear();
std::for_each(line_data.begin(),line_data.end(),[&
](MVector3F&
point){
MVector3F pt;
pt.m_y = point.m_y;
pt.m_x = point.m_x*std::cos(i*m_step);
pt.m_z = -point.m_x*std::sin(i*m_step);
next_line.push_back(pt);
});
m_round_data.push_back(next_line);
}
}
std::vector<MVector3F> m_line_datas;
std::vector<std::vector<MVector3F>>m_round_data;
double
m_step;
double
m_radio;
double
m_xrot;
double
m_yrot;
};
//=====================================
定义好数据类后我们就就可以定义3D类了:
//=====================================
class Round : public Window3D{
public:
Round(HINSTANCE hInstance, HINSTANCE hPreInstance, PSTR szCmdLine,
int iCmdShow, const std::wstring&
title, const std::wstring&
wndname)
:Window3D(hInstance,hPreInstance,szCmdLine,iCmdShow,title,wndname){
}
protected:
virtual
void DrawScreenGL();
virtual
void OnIdleFun();
};
void Round::DrawScreenGL(){
RoundData* data_ = dynamic_cast<RoundData*>(getData());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
glTranslatef(0.0f,0.0f,-5.0f);
glRotatef(data_->m_xrot, 1.0, 0.0, 0.0);
glRotatef(data_->m_yrot, 0.0, 1.0, 0.0);
glRotatef(data_->m_xrot, 0.0, 0.0, 1.0);
if(data_->m_round_data.empty())
data_->geneRound();
glColor3f(1.0,0.0,0.0);
glLineWidth(1);
auto it = data_->m_round_data.begin();
while(it!=data_->m_round_data.end()){
auto _it = it->begin();
glBegin(GL_LINE_STRIP);
while(_it != it->end()){
glVertex3f(_it->m_x,_it->m_y,_it->m_z);
++_it;
}
glEnd();
++it;
}
glPopMatrix();
}
void Round::OnIdleFun(){
RoundData* data_ = dynamic_cast<RoundData*>(getData());
if (data_->m_xrot >= 360){
data_->m_xrot = 0.0;
}
data_->m_xrot += 0.5;
if (data_->m_yrot >= 360){
data_->m_yrot = 0.0;
}
data_->m_yrot = +0.5;
}
//=============================
定义很简单,如同上一讲我们所说的,我们基本只需要重写DrawScreenGL()虚函数即可,但是这里我们想要球无休止的旋转下去,所以我们重写OnIdleFun(),这个函数其实在底层是被一个定时器控制的,并非真正的Cpu闲时才会调用的函数,只要这个函数存在,那么每隔10ms就会被执行一次,如果大家有疑问的话,可以回头去前面几讲中我们所说的源码,最后驱动函数一如既往的简单得不行:
//==========================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, char* cmdline,
int showcmd){
try{
MWindow* w = new Window;
Round w3d(hInstance, hPreInstance, cmdline, showcmd, L"
Test"
, L"
Test"
);
w3d.Attach(w);
RoundData data;
w3d.AddData(&
data);
w3d.CreateGLWindow(750, 450, 32, false);
w->run();
delete w;
return 0;
}
catch (const wchar_t* s){
MessageBox(nullptr, s, L"
Error"
, MB_OK);
}
}
//=================================
运行会发现一个无休止旋转的球形:
最后说一下上次发的面试题,很多同学说希望给出答案,因为这是公司的面试题,所以答案就不放出来了,如果大家细心的话会发现其实所有答案都能够在我们这个课程中找到,由于没把面试题放进目录中,所以可能过了就过了。。。。。
关于这个3D框架的使用其实是很简单的,相信大家已经看出来了,下一讲将会以一个小游戏来结束(这个游戏我自己还没玩通关过……..)
============================
回复d查看目录,具体情况目录有介绍
原文始发于微信公众号(
C/C++的编程教室
):第一百讲 使用MFrame3D(2)
|