Ogre应用之一:MFC+Ogre+OSI搭建

来源:互联网 发布:淘宝手机详情加链接 编辑:程序博客网 时间:2024/06/10 17:45

Ogre应用之一MFC+Ogre+OSI搭建

by:唐崇(theone7)

虫子||CSDN技术博客(http://blog.csdn.net/theone7)


引言:这系列的文章来自于学校毕业设计的选题—“3D售楼处”,在小组中我负责3D图形的渲染,采用的是Ogre引擎来做。由于在准备研究生,断断续续地用将近一个的月的时间完成了一个Demo。


一、将Ogre嵌入MFC框架

    用过Ogre的同学都知道:在SDK中,官方提供了一个很好的例子来介绍如何使用Ogre。而网上很多教程是直接继承例子中的类来实现想要的工作的,感觉这会多出很多不需要的东西。所以在我的Demo中是直接新建一个类,只加上自己想要的东西。(下面只列出了本文有关的代码)

class COGRE_Manager{//单例模式构建{{private:explicit COGRE_Manager();public:~COGRE_Manager();inline static COGRE_Manager& GetSingleton(){static COGRE_Manager m_ogreManager;return m_ogreManager;}//单例模式构建结束}}//<功能接口>:public://初始化bool StartUp(HWND m_hWnd, int width, int height ,HWND hMainWnd);//<数据接口>:public://< 获得私有成员变量>:        //ogre根对象        Ogre::Root* GetRoot(void) const {return mRoot;}//摄像机对象        Ogre::Camera* GetCamera(void) const {return mCamera;}        //场景管理器对象        Ogre::SceneManager* GetSceneManager(void) const {return mSceneMgr;}//渲染窗口对象        Ogre::RenderWindow* GetRenderWindow(void) const {return mWindow;}//<私有方法>:private:    //创建ogre根对象    void CreateRoot();    //设置渲染系统    void SetupRenderSystem();    //创建渲染窗口    void CreateRenderWindow(HWND m_hWnd, int width, int height);//渲染场景管理器      void ChooseSceneManager();//创建摄像机      void CreateCamera();//创建视口      void CreateViewport();//创建场景      void CreateScene();//创建帧监听对象      void CreateFrameListener(HWND hMainWnd);//<实现细目>:private:Ogre::Root* mRoot;Ogre::RenderWindow* mWindow;Ogre::SceneManager* mSceneMgr;Ogre::Camera* mCamera;COGRE_FrameListener* mFrameListener;Ogre::String mPluginsCfg;};


   当运行Ogre的例子时,会发现它不是基于Windows的。默认的创建渲染窗口根本就不需要Windows窗口,直接一个Create就好。无奈我的项目需要使用MFC框架,其实也不难,做好一下三件事就行。

1.渲染配置

  Ogre官方提供的例子中,会先弹出Oger配置框,再根据用户的选择进行渲染。既然用的是MFC框架,直接渲染到Windows窗口中,自然要把程序开始时蹦出的配置框去掉。自己把渲染系统参数设置好即可:

void COGRE_Manager::SetupRenderSystem(){// 设置渲染子系统Ogre::RenderSystem *rs = mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");mRoot->setRenderSystem(rs);// 设置配置选项rs->setConfigOption("Full Screen", "No");rs->setConfigOption("Video Mode", "1024 x 768 @ 32-bit colour");}

2.渲染窗口

  如果要使用Windows窗口作为Ogre的渲染窗口,则得取消Ogre默认自己创建窗口。Ogre支持采用外部窗口,这里需要将MFC中的View类作为渲染窗口。在创建渲染窗口时,需传入View的句柄作为参数:

void COGRE_Manager::CreateRenderWindow( HWND m_hWnd, int width, int height ){// root初始化的时候,设置false值来告知Root不用给我们自动创建渲染窗口// 这样我们可以MFC的窗口来进行渲染mRoot->initialise(false);Ogre::NameValuePairList miscParams; // 参数列表, 作为createRenderWindow函数的最后一个参数miscParams["externalWindowHandle"] = Ogre::StringConverter::toString((long)m_hWnd);mWindow = mRoot->createRenderWindow("OgreRenderWindow", width, height, false, &miscParams);}

3.渲染循环

  OGRE官方例子中使用的是startRendering()来让程序进入渲染循环的。此方法将消息交给了系统。这会与MFC的消息循环起冲突,比如说WM_PAINT和OGRE一起渲染。解决办法就是做到一个循环中来,正好OGRE有提供只渲染一帧的方法renderOneFrame();

将渲染一帧的函数放入到View类的OnDraw函数里面,用定时器来循环调用使其达到渲染循环的效果。

void COgreDemoView::OnDraw(CDC* /*pDC*/){   COgreDemoDoc* pDoc = GetDocument();   ASSERT_VALID(pDoc);   if (!pDoc)      return;   if(m_isFirstDraw)   {      //第一次画图时,初始化数据      m_isFirstDraw = FALSE;      GetClientRect(&m_ViewRect);      COGRE_Manager::GetSingleton().StartUp(m_hWnd,m_ViewRect.Width(),m_ViewRect.Height(),AfxGetApp()->GetMainWnd()->GetSafeHwnd());      SetTimer(1, 10, NULL);   }   if(m_isStart)   {      COGRE_Manager::GetSingleton().GetRoot()->renderOneFrame();   }}

在这里使用SetTimer设定一个定时器。再提供它的响应函数:

void COgreDemoView::OnTimer(UINT_PTR nIDEvent){   if(m_isStart)   {      COGRE_Manager::GetSingleton().GetRoot()->renderOneFrame();   }   CView::OnTimer(nIDEvent);}

  通过SetTimer设定的参数。该响应函数每10ms触发一次。这样,就可以达到10ms fps即100fps的渲染效果了。


  通过这三个方面,渲染的工作就已经实现了与MFC的结合。接下来,就是IOS的设定了。之前版本的OGRE是采用外部的CEGUI来负责IO处理的,不过最新版本已经不再用CEGUI了,而是用自身提供的IOS。这样一来也方便了我们的开发。另外提一句,本来是想直接使用MFC的消息处理机制来处理鼠标键盘IO的,但在做鼠标拾取时无奈的发现MFC获得的鼠标位置精确度太低了。所以还是老老实实的使用OIS。

二、设定OIS

  OIS在创建输入系统的时候,它要控制的窗口必须是顶层窗口,就是说你传view视图的窗口句柄给他不行,必须要MFC程序的主框架窗口。所以这样一来,我们要提供2个的窗口句柄给OGRE了。一个是View的句柄,作渲染用;一个是主框架窗口,作为键盘鼠标的IO处理。另外,要使用OIS,需创建一个监听类,就像Ogre案例中的一样它需继承 Ogre::FrameListener,OIS::MouseListener,OIS::KeyListener 这几个类。

嗯,下面是设定代码:

COGRE_FrameListener::COGRE_FrameListener(COGRE_Manager*manager,HWND hMainWnd):mIsRBDowm(false),mPreState(),misLBDown(false),mCurrentObject(),mCount(0),mTrayMgr(0){   mSceneMgr =manager->GetSceneManager();   mCamera =manager->GetCamera();   mWindow =manager->GetRenderWindow();   mRaySceneQuery =mSceneMgr->createRayQuery(Ogre::Ray());   size_t windowHnd = 0;   std::ostringstreamwindowHndStr;   OIS::ParamList pl;   mWindow->getCustomAttribute("WINDOW", &windowHnd);   windowHnd = (size_t)hMainWnd; // 这里这个窗口句柄就是我们传入的MFC主窗口   windowHndStr <<windowHnd;   // OIS的窗口必须要顶层窗口,所以只有传MFC的主窗口给他,传view就不行   pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));   // 设置鼠标显示和非游戏独占,这样鼠标可以显示在屏幕上并可以移动到窗口外   pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND")));   pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));   ShowCursor(0); //隐藏系统鼠标指针   // 键盘非游戏独占   pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));   pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));   mInputManager =OIS::InputManager::createInputSystem(pl); // 这样InputManager就建好了,但为了从键盘、鼠标、或是手柄中获得输入,还必须创建这些对象:   mKeyboard=static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard,true));   mMouse=static_cast<OIS::Mouse*>(mInputManager->createInputObject(OIS::OISMouse,true));   mKeyboard->setEventCallback(this); //键盘回调   mMouse->setEventCallback(this);      //鼠标回调//设置鼠标移动的范围{{   CRect temp;   ((CMainFrame *)AfxGetMainWnd())->GetActiveView()->GetWindowRect(temp);   mMouse->getMouseState().width  = temp.Width();   mMouse->getMouseState().height  = temp.Height();
//设置完毕}}   mTrayMgr = new OgreBites::SdkTrayManager("InterfaceName", mWindow, mMouse);   mTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);}

接下来要做的事就是在监听IO的函数做响应的处理了。按照自己的需求填写一下函数

   bool frameStarted(const Ogre::FrameEvent &evt);

   bool mouseMoved(const OIS::MouseEvent &e);

   bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonIDid);

   bool mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonIDid);

   bool keyPressed(const OIS::KeyEvent &e);

   bool keyReleased(const OIS::KeyEvent &e);

这样,OGRE+MFC+OSI就搭建好了。

下面是效果图: