第九十八讲 MFrame3D(4)

今天我们来开始2015年的第一章C++的内容,接去年我们说到的3D学习框架,在九十七讲里面我们说到怎么产生一个窗口,那么这一讲我们来看看怎么在那个窗口中放入3D物理,这一讲的内容比较多,我就权当大家都能够理解得过来,所以我可能会很少加注释说明(担心篇幅不够),依照惯例,我从头文件开始:
//========================================
//MWindow3D.h
/****************************
*

*
*
3D窗口基类头文件 *
*

*
****************************/
#pragma once
#include "

MWindow.h"


#include "

MLight.h"


#include "

MFog.h"


#include "

MMaterial.h"


#include "

MTexture.h"


#include "

MParticle.h"


#include "

MMouseEvent.h"


#include "

MKeyEvent.h"


#include "

MFont.h"

namespace Mengjin{

class MWindow3D

{

public:

MWindow3D(HINSTANCE hInstance,HINSTANCE hPreInstance,PSTR szCmdLine,

int iCmdShow,const std::wstring&

title,const std::wstring&

wndname);


MWindow3D();


virtual ~MWindow3D();


private:

MWindow3D(const Window3D&

);

//惯例,我们不该让窗口有复制或者赋值的能力

Window3D&

operator=(const Window3D&

);



public:

bool CreateGLWindow(int w,int h,int bits,bool fullscreen);

//创建3D窗口

void Attach(MWindow* window);

//绑定窗口,这个窗口就是我们上一讲产生的那个窗口

void IsBlend(GLfloat* blendcolor,bool blend);

//使用混合

void IsDepthTest(bool depth);

//使用深度测试

void AddLight(MLight* light);

//添加光照效果

void AddFog(MFog* fog);

//添加雾效果

void AddMaterial(MMaterial* material);

//添加材料

void AddTexture(const MTexture&

textura);

//添加纹理

void AddParticle(MParticle* particle);

//添加粒子系统

void AddData(MDataBase* data)
{ if(data) m_Data = data;

} //绑定数据

MTexture&

getTexture(){
return m_Texture;

}
//获取现在的纹理

FTGLPixmapFont* getPixmapFont(){

return m_DrawFont;

} //获取3D文字对象

protected:

virtual bool Init();

//初始化相关变量

virtual bool InitGL();

//初始化3D窗口

virtual
void Addinit(){ }

//附加初始化函数

virtual
void ReshapeGL(int w,int h);

//改变窗口时由框架调用

virtual
void DrawScreenGL();

//画3D界面

virtual
void KillWindow();

//关闭程序

virtual
void Winto3DPos(double x,double y);

//左面坐标转换到3D坐标

virtual
void OnIdleFun(){ };


protected:

MDataBase* getData(){
return m_Data;

}


private:

void NewFont();

//构建画笔,用于OpenGL屏幕上题字

void OpenBlend();

//开启混合效果

void OpenDepthTest();

//开启深度测试
private:

HWND m_hwnd;

//windows句柄

HGLRC m_hglrc;

//窗口着色描述句柄

HDC m_hdc;

//OpenGL渲染描述表句柄

HINSTANCE m_hInstance;

// 实例句柄

HINSTANCE m_hPreInstance;

PSTR m_CmdLine;

//命令行,暂时没发现用处,但为了和WinMain保持一致,先保留

int m_iCmdShow;

//显示窗口WS_SHOW显示,WS_HIDE隐藏

bool b_IsActive;

//窗口是否处于活动状态

bool b_IsFullscreen;

//是否全屏

std::wstring m_Title;

//窗口标题

std::wstring m_wndclassname;

//窗口类名

GLdouble m_fov;

//gluPerspective的可视角度

GLuint m_pixelform;

//像素格式

PIXELFORMATDESCRIPTOR m_pfd;

//像素描述表

//混合

GLfloat*

m_blend_color;


MDataBase*
m_Data;

//数据中心


protected:

FTGLPixmapFont*

m_DrawFont;

//绘制3D文字

MTexture

m_Texture;

//纹理

std::vector<MLight*>
m_Lights;

//光照

std::vector<MFog*>
m_Fogs;

//雾

std::vector<MMaterial*>
m_Materias;

//材料

std::vector<MParticle*>
m_Particle;

//粒子系统

bool

b_IsBlend;

//是否启用混合

bool

b_IsDepth;

//是否启用深度测试

GLdouble

m_height, m_width;


Window*
m_window;

//窗口

//辅助初始化Opengl函数

ADD_INIT

m_add_init_fun;

//辅助初始化

RECALL_WNDPROC

m_add_wndproc;

//辅助消息处理

};

}
//=======================

这个3D基类大致就像这样,为了方便大家理解,所以我在一个函数甚至每一个成员变量后面都添加了注释了,这样一来我就省去和大家解释每个对象作用的时间了,所以在大家确认明白个大意的时候我们就可以继续去看实现,同样,只要理解了原理,那么实现也就不难了。

//=======================

#include "

MWindow3D.h"

using namespace Mengjin;

//惯例,构造函数,其实这里可以不必要这么麻烦的,可以更简单一些,我们不必要这么多参数就可以,只是这里为了WinMain原型保持一致所以我这么写。
MWindow3D::MWindow3D(HINSTANCE hInstance,HINSTANCE hPreInstance,PSTR szCmdLine,int iCmdShow,const std::wstring&

title,const std::wstring&

wndname)
{

m_hInstance = hInstance;


m_hPreInstance = hPreInstance;


m_CmdLine = szCmdLine;


m_iCmdShow = iCmdShow;


m_Title = title;


m_wndclassname = wndname;


m_DrawFont = nullptr;


m_add_init_fun = nullptr;


m_add_wndproc = nullptr;


b_IsBlend = false;


b_IsDepth = true;


m_add_init_fun = std::bind(&

Window3D::Addinit,this);


m_Texture.clear();


NewFont();



}


MWindow3D::MWindow3D(){

if(!Init()){

throw L"

初始化失败!!!"

;


}

}


//初始化一些相关变量,其实下面这些变量我们大可以不要,因为我们在MWindow里面已经得到了初始化,这里也无所谓了
bool MWindow3D::Init(){

m_hInstance = nullptr;


m_hPreInstance = nullptr;


m_CmdLine = "

"

;


m_iCmdShow = SW_SHOW;


m_Title = L"

"

;


m_wndclassname = L"

"

;


m_DrawFont = nullptr;


m_add_init_fun = nullptr;


m_add_wndproc = nullptr;


b_IsBlend = false;


b_IsDepth = false;


m_add_init_fun = std::bind(&

MWindow3D::Addinit,this);


//我们将附加初始化函数给绑定给基类,这样的好处是我们不想重写初始化函数的时候我们只想添加一些东西这时我们只需要重写Addinit函数就可以满足我们的需求

m_Texture.clear();


NewFont();


return true;



}


//析构函数,清理垃圾回收资源都在这里面进行
MWindow3D::~MWindow3D(void)
{

if(m_DrawFont){

delete m_DrawFont;


m_DrawFont = nullptr;


}

m_Texture.clear();


std::for_each(m_Lights.begin(), m_Lights.end(), [](MLight* ptr){

if (ptr){

delete ptr;


ptr = nullptr;


}

});

std::for_each(m_Fogs.begin(), m_Fogs.end(), [](MFog* ptr){

if (ptr){

delete ptr;


ptr = nullptr;


}

});

std::for_each(m_Materias.begin(), m_Materias.end(), [](MMaterial* ptr){

if (ptr){

delete ptr;


ptr = nullptr;


}

});

std::for_each(m_Particle.begin(), m_Particle.end(), [](MParticle* ptr){

if (ptr){

delete ptr;


ptr = nullptr;


}


});

KillWindow();



}

//创建字体

void MWindow3D::NewFont(){

if(m_DrawFont)

return;

m_DrawFont = new FTGLPixmapFont("

C:\Windows\Fonts\simhei.ttf"

);


//其实这里的字体我们可以做得更加灵活一些,没必要写死,这里就留给需要修改的同学去修改吧

if(m_DrawFont == nullptr){

throw L"

创建字体失败!!!"

;


}

else{

m_DrawFont->FaceSize(20);


m_DrawFont->CharMap(ft_encoding_unicode);

//ft_encoding_unicode

}

}

//初始化3D环境,这个函数我们可以在自己的派生类中重写,也可以不用,大家看到其实我在里面为大家准备了好多东西的,所以除非我们有其他的特殊要求需要重写该函数才重写,如果只是补充初始化一些东西,我们可以选择重写Addinit
bool MWindow3D::InitGL(){

//首先我们将屏幕刷成黑色的,带有gl开头的函数都是OpenGL的API函数,这里不做多讲,大家可以参考OpenGL编程指南一书

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);


//加载纹理

if (m_Texture.open()){

glEnable(GL_TEXTURE_2D);


}

//检查开启光照

if (!m_Lights.empty()){

std::for_each(m_Lights.begin(), m_Lights.end(), [&

](MLight* light){

light->open();


});


}

//开启混合模式

OpenBlend();


//开启雾效果

if (!m_Fogs.empty()){

std::for_each(m_Fogs.begin(), m_Fogs.end(), [&

](MFog* fog){

fog->open();


});


}

glShadeModel(GL_SMOOTH);


glClearDepth(1.0f);


OpenDepthTest();


glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);


//最后检查派生类是否添加了新的初始化内容

if (m_add_init_fun)

m_add_init_fun();


return true;



}

//改变窗口大小时候该函数会得到调用,这个函数也是我们会重写的一个函数,因为我们可能会因为不同的场景使用不同的投影模式

void MWindow3D::ReshapeGL(int w,int h){

if(h == 0){

h = 1;


}

m_width = w;


m_height = h;


glViewport(0,0,w,h);


glMatrixMode(GL_PROJECTION);


glLoadIdentity();


gluPerspective(45.0,(GLfloat)w/(GLfloat)h,0.0f,100.0f);


glMatrixMode(GL_MODELVIEW);


glLoadIdentity();



}

//画屏幕,也就是说我们要在屏幕上显示什么我们就在该函数里面进行,所以我们只需要重写该函数即可
//这里虽然只是个基类,但是我在屏幕上画了一个紫色三角形,一个绿色正方向,还写下了“漂亮”二字,哈哈…….
//如大家所想,这里面全是使用OPenGL函数在作图

void MWindow3D::DrawScreenGL(){

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


static GLfloat m_xRot = 0.0f;


static GLfloat m_yRot = 0.0f;


glLoadIdentity();


glPushMatrix();


glColor3f(1.0f, 0.0f, 1.0f);


glTranslatef(-3.0f, 0.0f ,-6.0f);


glRotatef(m_xRot, 1.0f, 0.0f, 0.0f);


glBegin(GL_TRIANGLES);


glVertex3f(0.0f, 1.0f, 0.0f);


glVertex3f(-1.0f, -1.0f, 0.0f);


glVertex3f(1.0f, -1.0f, 0.0f);


glEnd();


glPopMatrix();


glLoadIdentity();


glPushMatrix();


glTranslatef(0.0f,0.0f,-6.0f);


glRasterPos3f(0.0f,0.0f,0.0f);


m_DrawFont->Render(L"

漂亮"

);


glTranslatef(1.5f, 0.0f, 0.0f);


glRotatef(m_yRot, 0.0f, 1.0f, 0.0f);


glColor3f(0.0f, 1.0f, 0.5f);


glBegin(GL_QUADS);


glVertex3f(1.0f, 1.0f, 0.0f);


glVertex3f(-1.0f, 1.0f, 0.0f);


glVertex3f(-1.0f, -1.0f, 0.0f);


glVertex3f(1.0f, -1.0f, 0.0f);


glEnd();


glPopMatrix();


m_xRot += 2.0f;


m_yRot += 2.0f;



}

//关闭程序,清除资源

void MWindow3D::KillWindow(){

//如果当前全屏,退出全屏

if(b_IsFullscreen){

ChangeDisplaySettings(nullptr,0);


ShowCursor(true);


}

//如果有资源未释放,释放资源

if(m_hglrc){

if(!wglMakeCurrent(nullptr,nullptr)){

throw L"

Release DC or RC fail!!"

;


}

if(!wglDeleteContext(m_hglrc)){

throw L"

Release RC fail!!!"

;


}

m_hglrc = nullptr;


}

if(m_hdc &

&

!ReleaseDC(m_hwnd,m_hdc)){

throw L"

Release DC fail!!!"

;


m_hdc = nullptr;


}

if(m_hwnd &

&

!DestroyWindow(m_hwnd)){

throw L"

Release HWND fai!!!"

;


m_hwnd = nullptr;


}

if(!UnregisterClass(m_wndclassname.c_str(),m_hInstance)){

throw L"

close window fail!!!"

;


m_hInstance = nullptr;


}

}

//绑定windows窗口,也就是绑定我们在上一讲所产生的那个窗口,他就是我们这里画出的3D场景的容器

void MWindow3D::Attach(MWindow* window){

m_window = window;



}

//是否启动混合模式

void MWindow3D::IsBlend(GLfloat* blendcolor,bool blend){

m_blend_color = blendcolor;


b_IsBlend = blend;



}


void MWindow3D::OpenBlend(){

if (b_IsBlend){

glEnable(GL_BLEND);


glColor4fv(m_blend_color);


glBlendFunc(GL_SRC_ALPHA, GL_ONE);


}

}

//是否使用深度测试

void MWindow3D::IsDepthTest(bool depth){

b_IsDepth = depth;



}


void MWindow3D::OpenDepthTest(){

if (b_IsDepth){

glEnable(GL_DEPTH_TEST);


glDepthFunc(GL_LEQUAL);


}

}

//创建OpenGL窗口
bool Window3D::CreateGLWindow(int w,int h,int bits,bool fullscreen){

static
int ctn = 0;


++ctn;


DWORD dwExStyle;

//窗口扩展风格

DWORD dwStyle;

//窗口普通风格

if(fullscreen){

DEVMODE dmScreenSetting;


memset(&

dmScreenSetting,0,sizeof(dmScreenSetting));


dmScreenSetting.dmSize = sizeof(dmScreenSetting);


dmScreenSetting.dmPelsWidth =w;


dmScreenSetting.dmPelsHeight = h;


dmScreenSetting.dmBitsPerPel = bits;


dmScreenSetting.dmFields = DM_BITSPERPEL | DM_PELSHEIGHT | DM_PELSWIDTH;

if(ChangeDisplaySettings(&

dmScreenSetting,CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL){

if(MessageBox(nullptr,L"

FullScreen module fail!!!nPlease select window module!!!"

, L"

Warning"

,MB_YESNO|MB_ICONEXCLAMATION) == IDYES){

fullscreen = false;


if (ctn >= 2)

return true;


}

else{

MessageBox(nullptr,L"

Program will close"

,L"

Error"

,MB_OK);


return false;


}

}

}

else{

if (ctn >= 2)

return true;


}

if(fullscreen){

dwExStyle = WS_EX_APPWINDOW;


dwStyle = WS_POPUP;


ShowCursor(false);


}

else{

dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;


dwStyle = WS_OVERLAPPEDWINDOW;


ShowCursor(true);


}

RECT windowRect;


windowRect.left = 0;


windowRect.top = 0;


windowRect.bottom = (long)h;


windowRect.right = (long)w;

AdjustWindowRectEx(&

windowRect,dwStyle,false,dwExStyle);


if(m_hInstance == nullptr){

m_hInstance = GetModuleHandle(nullptr);


}

if(m_window==nullptr){

throw L"

Unable create window,Please Attach window"

;


return false;


}

//设置窗口相关变量

m_window->sethInstance(m_hInstance);


m_window->setCmdLine(m_CmdLine);


m_window->setExStyle(dwExStyle);


m_window->setShowCmd(m_iCmdShow);


m_window->setStyle(dwStyle);


m_window->setPrehInstance(nullptr);


m_window->setTitle(m_Title);


m_window->setWndclassName(m_wndclassname);


m_window->setWindowWidth(w);


m_window->setWindowHeight(h);

//构造窗口,同时获取句柄

if(!(m_hwnd = m_window->NewWindow())){

KillWindow();


return false;


}

static PIXELFORMATDESCRIPTOR pdf ={

sizeof(m_pfd),

//像素描述表大小

1,

//描述表版本,通常是1

PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //支持类型

PFD_TYPE_RGBA,

//颜色类型,这里选择RGB

bits,

//颜色深度

0,0,0,0,0,0,

//忽略各个颜色位,每个RGBA颜色缓冲区中RGB色位平面的数目和偏移数

0,

0,

0,0,0,0,0,

//累积缓冲区RGBA的色位平面数目

32,

//缓存深度

0,

//模板缓存

0,

//辅助缓存

PFD_MAIN_PLANE,

//主层

0,

//保留位

0,0,0

};


m_pfd = pdf;

if(!(m_hdc = GetDC(m_hwnd))){

KillWindow();


throw L"

cann,t create PFD_TYPE!!"

;


return false;


}

m_window->SetHdc(m_hdc);


if(!(m_pixelform = ChoosePixelFormat(m_hdc,&

m_pfd))){

KillWindow();


throw L"

Unable set pix!!!"

;


return false;


}

if(!SetPixelFormat(m_hdc,m_pixelform,&

m_pfd)){

KillWindow();


throw L"

Unable set pix!!!!"

;


return false;


}

if(!(m_hglrc = wglCreateContext(m_hdc))){

KillWindow();


throw L"

Unable set pix!!!"

;


return false;


}

if(!wglMakeCurrent(m_hdc,m_hglrc)){

KillWindow();


throw L"

Unable set pix!!!!"

;


return false;


}

m_window->show();


SetForegroundWindow(m_hwnd);


SetFocus(m_hwnd);


ReshapeGL(w,h);


if (!InitGL()){

KillWindow();


throw L"

Unable set pix!!!!!"

;


return false;


}

//将下面几个函数注册给底层,由框架调用

m_window->RegisterDrawScreen(std::bind(&

Window3D::DrawScreenGL,this));


m_window->RegisterFunInitGL(std::bind(&

Window3D::InitGL, this));


m_window->RegisterReShape(std::bind(&

Window3D::ReshapeGL,this,std::placeholders::_1,std::placeholders::_2));


m_window->RegisterOnIdleFun(std::bind(&

Window3D::OnIdleFun,this));


return true;



}

//添加光照

void MWindow3D::AddLight(MLight* light){

if (light){

m_Lights.push_back(light);


}

}

//添加雾

void MWindow3D::AddFog(MFog* fog){

if (fog){

m_Fogs.push_back(fog);


}

}

//添加材料,让你的图形看上去更像真实的东西

void MWindow3D::AddMaterial(MMaterial* material){

if (material){

m_Materias.push_back(material);


}

}

//添加纹理

void MWindow3D::AddTexture(const MTexture&

textura){

m_Texture = textura;



}

//添加粒子

void MWindow3D::AddParticle(MParticle* particle){

if (particle)

m_Particle.push_back(particle);



}

//======================================

ok,我们将上一讲的驱动程序修改一下:
//=====================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, char* cmdline,
int showcmd){


try{

MWindow* w = new Window;


MWindow3D w3d(hInstance, hPreInstance, cmdline, showcmd, L"

Test"

, L"

Test"

);


w3d.Attach(w);


w3d.CreateGLWindow(750, 450, 32, false);


w->run();


delete w;


return 0;


}

catch (const wchar_t* s){

MessageBox(nullptr, s, L"

Error"

, MB_OK);


return -1;


}
}
//====================================
再看看运行效果,3D画面是不是出来了呢?

第九十八讲  MFrame3D(4)

第九十八讲  MFrame3D(4)




到这里,我们这个框架算是完成了,不过关于使用,可能因为我表述不是很清楚,所以接下来我会用这个框架来给大家举几个例子,我总觉得例子得效果比我表述的要来得实在得多。

==============================
回复D直接查看目录,回复相应的数字查看对应章节


原文始发于微信公众号(

C/C++的编程教室

):第九十八讲 MFrame3D(4)

|

发表评论