文章参考了《Ogre嵌入MFC傻瓜完全教程》(http://blog.csdn.net/guoyk1990/article/details/25915065)和Ogre官方网站上的示例(http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Setting+Up+An+Application+-+Visual+Studio&structure=Development)。实际操作过程中出现了一些问题,故将全部工作完整地记录下来,以备不时之需,也给广大朋友一个参考。记住:即便是“傻瓜完全教程”,也不可能完全照搬。
一、准备工作
本人的配置Windows7 64位系统、VS2010(32位)、OgreSDK_vc10_v1-8-1
下载OGRESDK:http://www.ogre3d.org/download/sdk ,并运行 安装到 D盘根目录。
二、环境变量配置
官方示例中采用cmd命令进行配置,本人不善于这种方式,操作起来没有安全感。故采用更直观的方式:
在用户变量和系统变量中均新建一项 OGRE_HOME 值为:D:\OgreSDK_vc10_v1-8-1
在path中添加一项:%OGRE_HOME%
本人最初仅仅是在系统变量中新建了该项,但在运动程序时,发现配置并没有成功,干脆两种变量都加,问题解决了。
配置好环境变量之后要重启电脑。
三、建立MFC单文档应用程序
1. 工程名称:OgreTest2,配置如下:
2. 项目属性配置
进入属性页,选择“所有配置”,
配置属性—>常规
输出目录:bin\$(Configuration)\
中间目录:obj\$(Configuration)\
mfc的使用:使用标准 Windows 库
字符集:使用多字节字符集
配置属性—>调试
命令: $(OGRE_HOME)\Bin\$(Configuration)\$(ProjectName).exe
工作目录: $(OGRE_HOME)\Bin\$(Configuration)
配置属性—>C/C++—>常规
附加包含目录
$(OGRE_HOME)\include
$(OGRE_HOME)\include\OIS
$(OGRE_HOME)\include\OGRE
$(OGRE_HOME)\Samples\Common\include
$(OGRE_HOME)\boost
配置属性—>C/C++—>预处理器
预处理器定义
添加一项:_AFXDLL
配置属性—>链接器—>常规
附加目录库:
$(OGRE_HOME)\lib\$(Configuration)
$(OGRE_HOME)\boost\lib
配置 切换为“Debug”:
配置属性—>链接器—>输入
附加依赖项:
OgreMain_d.lib
OIS_d.lib
配置 再切换为“Release”:
配置属性—>链接器—>输入
附加依赖项:
OgreMain.lib
OIS.lib
至此,完成配置工作,在分别在DEBUG和RELEASE模式下,点击生成命令,成功!
四、渲染场景至MFC窗口
1. 项目->添加类->C++类
添加的类名为“CMyOgreApp”,源代码如下:
头文件
#pragma once
#include "ogre.h"
#include "OgreConfigFile.h"
#include "OgreFrameListener.h"
#include "OgreStringConverter.h"
#include "OIS.h"
#include <SdkTrays.h>
#include <SdkCameraMan.h>
#include "MyFrameListener.h"
using namespace Ogre;
class CMyOgreApp
{
public:
CMyOgreApp(void);
~CMyOgreApp(void);
Ogre::Root* getRoot(void) const {return mRoot;}
Ogre::Camera* getCamera(void) const {return mCamera;}
Ogre::SceneNode* getCamNode(void) const {return mCamNode;}
Ogre::SceneManager* getSceneManager(void) const {return mSceneMgr;}
Ogre::RenderWindow* getRenderWindow(void) const {return mWindow;}
bool go(CRect rt, HWND hWnd);
void createRoot(void);
void setupResources(void);
void setupRenderSystem(void);
void createRenderWindow(HWND m_hWnd, int width, int height);
int initializeResourceGroups(void);
void chooseSceneManager(void);
void createCamera(void);
void createViewport(void);
void createScene(void);
void createFrameListener(void);
class CMyFrameListener* mListener;
private:
Ogre::Root* mRoot;
Ogre::RenderWindow* mWindow;
Ogre::SceneManager* mSceneMgr;
Ogre::Camera* mCamera;
Ogre::SceneNode* mCamNode;
OgreBites::SdkCameraMan* mCameraMan; // basic camera controller
Ogre::Light* light;
};
源文件
#include "StdAfx.h"
#include "MyOgreApp.h"
CMyOgreApp::CMyOgreApp(void)
{
}
CMyOgreApp::~CMyOgreApp(void)
{
}
bool CMyOgreApp::go(CRect rt, HWND hWnd)
{
createRoot();
setupResources();
setupRenderSystem();
createRenderWindow(hWnd, rt.Width(), rt.Height());
chooseSceneManager();
createCamera();
createViewport();
initializeResourceGroups();
createScene();
createFrameListener(); //创建侦听
return true;
}
void CMyOgreApp::createRoot(void)
{
// mRoot = new Ogre::Root("plugins_d.cfg"); //debug模式用
//已将plugins_d.cfg文件名改成了plugins.cfg,这样一来,无论是DEBUG还是RELEASE模式,都不用修改代码
mRoot = new Ogre::Root("plugins.cfg"); //release模式用
}
void CMyOgreApp::setupResources(void)
{
// Load resource paths from config file
Ogre::ConfigFile cf;
// cf.load("resources_d.cfg"); //debug模式用
//已将plugins_d.cfg文件名改成了plugins.cfg,这样一来,无论是DEBUG还是RELEASE模式,都不用修改代码
cf.load("resources.cfg"); //release模式用
// Go through all sections & settings in the file
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
Ogre::String secName, typeName, archName;
while (seci.hasMoreElements())
{
secName = seci.peekNextKey();
Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
Ogre::ConfigFile::SettingsMultiMap::iterator i;
for (i = settings->begin(); i != settings->end(); ++i)
{
typeName = i->first;
archName = i->second;
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
archName, typeName, secName);
}
}
}
void CMyOgreApp::setupRenderSystem(void)
{
// mRoot->showConfigDialog();
// mRoot->restoreConfig();
//if (!mRoot->restoreConfig() && !mRoot->showConfigDialog())
// throw Ogre::Exception(1, "未选择渲染系统","setupRenderSystem()");
RenderSystemList* rl = &const_cast<RenderSystemList&>(mRoot->getAvailableRenderers());
RenderSystem* rSys = NULL;
RenderSystemList::iterator it = rl->begin();
while (it!=rl->end())
{
if (-1 != (*it)->getName().find("OpenGL Rendering Subsystem"))
// if (-1 != (*it)->getName().find("Direct3D9 Rendering Subsystem"))
{
rSys = (RenderSystem*)(*it);
break;
}
++it;
}
rSys->setConfigOption("Full Screen","Yes");
rSys->setConfigOption("VSync","No");
mRoot->setRenderSystem(rSys);
}
void CMyOgreApp::createRenderWindow(HWND m_hWnd, int width, int height)
{
mRoot->initialise(false);//禁止ogre创建新的渲染窗口,而使用MFC的窗口
NameValuePairList misc;
misc["externalWindowHandle"] = StringConverter::toString((int)m_hWnd);
mWindow = mRoot->createRenderWindow("OgreRenderWindow", width, height, false, &misc);
}
int CMyOgreApp::initializeResourceGroups(void)
{
TextureManager::getSingleton().setDefaultNumMipmaps(5);
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
return 0;
}
void CMyOgreApp::chooseSceneManager(void)
{
mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);
}
void CMyOgreApp::createCamera(void)
{
// Create the camera
mCamera = mSceneMgr->createCamera("PlayerCam");
// Position it at 500 in Z direction
mCamera->setPosition(Ogre::Vector3(0,0,80));
// Look back along -Z
mCamera->lookAt(Ogre::Vector3(0,0,-300));
mCamera->setNearClipDistance(5);
mCameraMan = new OgreBites::SdkCameraMan(mCamera); // create a default camera controller
}
void CMyOgreApp::createViewport(void)
{
// Create one viewport, entire window
Viewport* vp = mWindow->addViewport(mCamera);
vp->setBackgroundColour(ColourValue(0,0,0));
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
}
void CMyOgreApp::createScene(void)
{
//添加背景
mSceneMgr->setSkyDome(true, "Examples/CloudySky",6, 8);
Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
headNode->attachObject(ogreHead);
// Set ambient light
mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
// Create a light
Ogre::Light* l = mSceneMgr->createLight("MainLight");
l->setPosition(20,80,50);
}
//创建帧监听
void CMyOgreApp::createFrameListener(void)
{
mListener= new CMyFrameListener(mWindow, mCamera, mSceneMgr, light);
mRoot->addFrameListener(mListener);
}
2. 在View类的头文件中包含CMyOgreApp 的头文件
#include "MyOgreApp.h"
并声明两个变量
public:
// ogre主类
CMyOgreApp m_ogreApp;
// 初次渲染
bool m_firstDraw;
3. 在View类的构造函数中将m_firstDraw 初始化为 true。
4. 在View类的OnDraw函数中添加下如下代码
void COgreTest2View::OnDraw(CDC* /*pDC*/)
{
COgreTest2Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
//Ogre渲染
try {
if(m_firstDraw)
{
m_firstDraw = false;
CRect rect;
GetClientRect(&rect);
m_ogreApp.go(rect,m_hWnd);//传入MFC主窗口句柄
SetTimer(1,20,0);
}
m_ogreApp.getRoot()->renderOneFrame();
} catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
std::cerr << "An exception has occured: " <<
e.getFullDescription().c_str() << std::endl;
#endif
}
}
5. 添加WM_TIMER和WM_ERASEBKGND消息处理函数,代码分别如下 :
void COgreTest2View::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (1 == nIDEvent)
{
m_ogreApp.getRoot()->renderOneFrame();
}
CView::OnTimer(nIDEvent);
}
BOOL COgreTest2View::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
return TRUE;
}
五、添加事件处理方法,实现人机交互
1. 添加一个帧监听类:CMyFrameListener,代码如下:
头文件
#pragma once
#include "ogre.h"
#include "OgreConfigFile.h"
#include "OgreFrameListener.h"
#include "OgreStringConverter.h"
#include "OIS.h"
#include "MyOgreApp.h"
#include <OISEvents.h>
#include <OISInputManager.h>
#include <OISKeyboard.h>
#include <OISMouse.h>
#include <SdkTrays.h>
#include <SdkCameraMan.h>
using namespace Ogre;
class CMyFrameListener: public FrameListener, public OIS::MouseListener,public Ogre::WindowEventListener, public OIS::KeyListener
{
public:
CMyFrameListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr, Light* light);
~CMyFrameListener(void);
bool frameRenderingQueued(const Ogre::FrameEvent& evt);
bool mouseMoved(const OIS::MouseEvent &e);
bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id);
bool mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id);
bool keyPressed(const OIS::KeyEvent &e);
bool keyReleased(const OIS::KeyEvent &e);
protected:
Ogre::SceneManager *mSceneMgr;//场景管理器
Ogre::SceneNode* mCamNode;//摄像机节点
Ogre::Camera* mCamera;//摄像机
Ogre::RenderWindow* mWindow;//渲染窗口
Ogre::Light* light;//灯光
OIS::Keyboard* mKeyboard;//键盘
OIS::Mouse* mMouse; //鼠标
OIS::InputManager* mInputManager;//输入管理器
// OgreBites::SdkTrayManager* mTrayMgr;
OgreBites::SdkCameraMan* mCameraMan; // basic camera controller
bool mRBtdown;
};
源文件
#include "StdAfx.h"
#include "MyFrameListener.h"
CMyFrameListener::CMyFrameListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr, Light *l): mMouse(0),
mKeyboard(0), mInputManager(0), mWindow(win), mCamera(cam), light(l)
{
mRBtdown = false;
mCamNode=cam->getParentSceneNode();
mSceneMgr = sceneMgr;
size_t windowHnd = 0;
std::ostringstream windowHndStr;
OIS::ParamList pl;
mCameraMan = new OgreBites::SdkCameraMan(mCamera);
windowHnd = (size_t )AfxGetMainWnd()->GetSafeHwnd(); // 这里这个窗口句柄就是传入的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")));
// 键盘非游戏独占
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);
mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));
mMouse->setEventCallback(this);
mKeyboard->setEventCallback(this);
}
CMyFrameListener::~CMyFrameListener(void)
{
mInputManager->destroyInputObject(mMouse);
mInputManager->destroyInputObject(mKeyboard);
OIS::InputManager::destroyInputSystem(mInputManager);
mInputManager = 0;
}
bool CMyFrameListener::frameRenderingQueued(const Ogre::FrameEvent& e){
mMouse->capture();
mKeyboard->capture();
mCameraMan->frameRenderingQueued(e);
return true;
}
bool CMyFrameListener::mouseMoved(const OIS::MouseEvent &e)
{
if (mRBtdown)
{
mCameraMan->injectMouseMove(e);
}
return true;
}
bool CMyFrameListener::mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
// mCameraMan->injectMouseDown(e, id);
if (id == OIS::MB_Right)
{
mRBtdown = true;
}
return true;
}
bool CMyFrameListener::mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
// mCameraMan->injectMouseUp(e, id);
if (id == OIS::MB_Right)
{
mRBtdown = false;
}
return true;
}
//键盘响应
bool CMyFrameListener::keyPressed(const OIS::KeyEvent &e)
{
mCameraMan->injectKeyDown(e);
return true;
}
bool CMyFrameListener::keyReleased(const OIS::KeyEvent &e)
{
mCameraMan->injectKeyUp(e);
return true;
}
2. 为了实现监听与响应,需要修改CMyOgreApp类,而这些工作在上面贴出的代码中已经完成了,这里再简单说明一下:
在CMyOgreApp类的头文件中包含监听类
#include "MyFrameListener.h"
声明一个CMyFrameListener类的变量
class CMyFrameListener* mListener;
声明一个函数,函数的实现已在上面的代码中贴出来了
void createFrameListener(void);
在go()函数中调用createFrameListener()
3. 需要特别指出的是setupRenderSystem()函数的修改,本项目默认以OpenGL进行渲染,所以省略了渲染选择对话框
代码如下:
void CMyOgreApp::setupRenderSystem(void)
{
RenderSystemList* rl = &const_cast<RenderSystemList&>(mRoot->getAvailableRenderers());
RenderSystem* rSys = NULL;
RenderSystemList::iterator it = rl->begin();
while (it!=rl->end())
{
if (-1 != (*it)->getName().find("OpenGL Rendering Subsystem"))
// if (-1 != (*it)->getName().find("Direct3D9 Rendering Subsystem"))
{
rSys = (RenderSystem*)(*it);
break;
}
++it;
}
rSys->setConfigOption("Full Screen","Yes");
rSys->setConfigOption("VSync","No");
mRoot->setRenderSystem(rSys);
}
如果想要弹出渲染选择对话框,则代码可以修改为如下:
void CMyOgreApp::setupRenderSystem(void)
{
mRoot->showConfigDialog();
}
4. 为了保证在DEBUG和RELEASE模式下,程序都可以正确运行,本人将D:\OgreSDK_vc10_v1-8-1\bin\debug文件夹中的两个文件的名称改了:
plugins_d.cfg 改成了 plugins.cfg
resources_d.cfg 改成了 resources.cfg
至此,全部工作就完成了,生成并运行。