基于MFC多文档多视图结构的OGRE指北针程序

基于MFC多文档多视图结构的OGRE指北针程序

0.前沿

作者: 化凡

QQ: 371691096

Mail:[email protected]

1.  功能描述

本程序作为OGREMFC结合,欲实现以下几个功能:

(1)       MFC是多文档多视图结构;

(2)       每打开一个子窗口,就显示一个不同的场景,可以在不同子窗口场景中任意添加几何体,可以扩充为模型浏览器和场景编辑器;

(3)       每个子窗口包含一个主场景,用于显示模型;包含一个指北针场景,专门用于显示场景坐标系,固定在窗口左下角显示。

(4)       可以任意打开、关闭一个子窗口。

(5)       实现效果图:

2.  关键技术点

(1)       MFC多文档多视图框架(不是本文重点);

(2)       OGRERoot对象定义时机和位置;

(3)       SceneManagerCameraViewport的动态添加和释放;

(4)       多个视口的背景融合;

(5)       深度缓存控制(扩展到颜色缓存、模板缓存的控制)

3.  实现步骤

3.1 MFC多文档多视图实现

a)  新建一个MFC多文档工程;

b)  新建三个类CMyDoc : public CDocumentCMyView : public CViewCMyChildFrame : public CMDIChildWnd

c)  添加菜单资源IDR_MENU_MY、如果每个子窗口需要工具栏,则添加工具栏IDR_TOOLBAR_MY

d)  CXXApp里添加变量:

     CMultiDocTemplate* pDocTemplate;     //管理文档一

     CMultiDocTemplate* pMyDocTemplate;   //管理文档二

e)  后台CXXApp::InitInstance()里添加类似这样的代码:

//文档一

pDocTemplate = new CMultiDocTemplate(IDR OgreMDITYPE,/*工程资源ID*/

         RUNTIME_CLASS(COgreMDIDoc),

         RUNTIME_CLASS(CChildFrame), // custom MDI child frame

         RUNTIME_CLASS(COgreMDIView));

     //文档二

pMyDocTemplate= new CMultiDocTemplate(IDR_MENU_MY, /*新加文档的菜单ID*/

         RUNTIME_CLASS(CMyDoc),

         RUNTIME_CLASS(CMyChildFrame), // custom MDI child frame

         RUNTIME_CLASS(CMyView));

     注意:包含对应的头文件!

f)   每个子窗口添加自己的工具栏,类似下面代码:

int CMyChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

  if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)

       return -1;

 

  if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP

       | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||

       !m_wndToolBar.LoadToolBar(IDR_TOOLBAR_MY))

  {

       return -1;      // fail to create

  }

  m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

  EnableDocking(CBRS_ALIGN_ANY);

  DockControlBar(&m_wndToolBar);  

  return 0;

}

----------------到现在运行程序,就可以看到MFC多文档多视图的效果了-------------------------

3.2 OGRERoot对象定义时机和位置

参考OGRE提供的ExampleApplication例子,自己加以改写封装,需要注意的是,整个应用程序只需要一个Root对象,只需要一次初始化,然后可以动态地添加和删除SceneManagerCameraViewport等对象。具体类设计参考程序源码。

本文封装了一个OgreDemo类,然后定义一个全局对象:

OgreDemo app;

Root *mRoot;//方便引用Root对象

3.3 SceneManagerCameraViewport的动态添加和释放

每次需要动态添加和释放的信息单独设计了一个类,叫class MDINode,动态添加操作通过函数MDINode * OgreDemo::addMDI(HWND hWnd,int w,int h,HWND hMainWnd,String wName)来实现,具体看代码。大致思路是:

(a)         第一次打开子窗口,需要对OGRE做整体初始化,然后新建MDINode节点;

(b)         以后每次开打子窗口,直接新建MDINode节点即可;

(c)         MDINode节点释放,在对应的CMyView析构函数里;

(d)         每次添加节点主要包含“基本场景的创建及系列设置”“坐标系场景的创建及系列设置”;

3.4多个视口的背景融合

对每个子窗口,含有两套SceneManager、两个Camera和两个Viewport,但是这些信息都显示在一个mWindow里。如果不经过特殊设置,后面的场景会遮挡前面的场景,本文中,坐标系场景会遮挡住前面的场景。这是需要解决一个问题:多视口背景融合

只要理解三维图形渲染流程,这个问题很容易解决。直观的想法是,在渲染后面场景的时候,不清空颜色缓存,那么后续渲染直接写到颜色缓存,那么背景就会存在了,从而达到融合的效果。因此,在定义第二个视口的时候,加上这句代码,即可实现视口背景融合问题:

vp->setClearEveryFrame(false);//这个设置很重要,实现和帧缓存混合

3.5深度缓存控制(扩展到颜色缓存、模板缓存的控制)

视口背景融合问题结局了,似乎任务已经完成了,但是添加物体后,把物体移动到坐标系附近,发现坐标系被遮挡住了。这肯定不是我们想要的效果。耐心分析一下不难想出解决方法:如果在绘制第二个场景的时候,把深度测试关掉,不就可以了吗?!答案确实是这样的。看似简单的问题,但操作起来却无从下手。

由于OGRE做了封装,整个渲染流程被隐藏起来了,真要自己控制,还真不简单。于是,我从网上下载了OGRE的元代码,仔细分析几个重要类的源代码,尝试过用RenderSystem对象进行设置,但是失败了,后来把焦点放在SceneManager_setPass函数,里面有段代码比较符合我的意思mDestRenderSystem->_setDepthBufferCheckEnabled(pass->getDepthCheckEnabled())可以看出这个好像是对深度缓存就行操作,通过实验确实是这样的

那么为什么会这样呢?这段代码明明是关于材质方面设置的,怎么会影响到深度缓存控制呢!对,我也有这样的疑问。但是事实就是这样的。我的猜测解释是:OGRE会给每个模型施加材质,如果你没有显示给一个模型添加纹理,OGRE会用模型纹理帮你添加上,但是你又看不出来,可能OGRE为了统一管理吧。然后关于深度缓存、颜色缓存等方面的控制,主要是通过纹理通道控制的,所以,必须通过设置材质通道的深度缓存控制属性,来控制深度缓存的读写。由于坐标系没有显示使用纹理,因此为了禁用深度测试,必须设置默认材质的通道属性,代码如下:

         MaterialPtr mDefault = MaterialManager::getSingleton().getDefaultSettings();

Pass *dPass=mDefault->getTechnique(0)->getPass(0);

dPass->setDepthCheckEnabled(false);

4.  总结

到此,整个程序的框架和关键点都介绍完毕,具体实现可参考代码。需要说明的是,用OGRE引擎开发东西,需要首先对OGRE的机制有点了解,对三维图形的渲染流程和基本原理有所掌握,这样在遇到实际问题时,才可以比较容易地解决。再者,下载一套OGRE的代码,遇到问题时,分析它的源代码,也有助于解决问题。

你可能感兴趣的:(VC,MFC,mfc,文档,class,工具,menu,图形)