使用WTL+OGRE编写3D程序(1) - 基本框架

来源:互联网 发布:js 判断是否是正整数 编辑:程序博客网 时间:2024/06/05 17:25

OGRE是一款十分出色的开源图像引擎,很适合游戏开发爱好者使用。但是本文并不是主要针对游戏开发,而是主要针对实验室的3D研究人员。据我所知,很多从事3D渲染研究的实验室的研究生、博士生都使用基本的3D API,包括Direct3D和OpenGL进行开发。这样做的原因其实也很明显:使用基本的3D API可以对渲染过程进行最好、最细致的控制。但是,对于需要展示渲染结果的项目和研究来说,使用基本的API开发显然时间耗费较长,而且很多细节都需要亲力亲为,开发难度也较大。

       使用OGRE可以最大限度的减少对琐碎的渲染细节的关注,或者说让我们集中更多的经历在更需要关注的细节上(80-20定理)。除了一个方面——GUI,OGRE并没有自己的GUI系统(Overlay其实已经没有什么用处),大家都知道使用CEGUI可以配合OGRE做出很漂亮的界面,但是对于研究项目来说,即没有必要,也不可能(很显然,因为研究项目一般都缺少美工,更不会花时间在界面上,这在教授们眼中是一钱不值得)。从另一方面,CEGUI并不是那么好用,和VC提供的即拖即放的界面设计对比,CEGUI还是比较垃圾。所以本文的题目是WTL+OGRE,相信很明白了:OGRE处理图像渲染,WTL/ATL负责界面处理。

        ——写给MFC程序员:如果你精通MFC,读这篇文章不会碰到任何问题,只要记住下面的代码确实不是MFC,而是WTL/ATL。

        废话半天了,进入正题。要想将OGRE和WTL一起使用,最重要的问题就是渲染窗口的创建和窗口消息的处理,要知道我们写的不是全屏程序而是窗口程序,而且要把图形显示在一个控件或对话框上,这和OGRE的默认程序(ExampleAppliation)是不同的。

        先看一下基本框架:

       为了将图形渲染在一个控件上,我们需要首先建立一个自定义控件的模版 COgreSceneBoxImpl,熟悉WTL的兄弟都知道这种常用的写法了,下面的代码展示的是一个基本框架,其中各个函数的处理过程我都暂时删掉了,后面会说到:

 

#ifndef _DRACULAX_OGRE_SCENEBOX_H
#define _DRACULAX_OGRE_SCENEBOX_H

#pragma warning(disable:4819)

#include <atlctrls.h>
#include <atlmisc.h>
#include <Ogre.h>

using namespace Ogre;

#ifndef FALSE_RETURN
#define FALSE_RETURN(h, r) if(!h) return r;
#endif

#ifndef SAFE_DELETE
#define SAFE_DELETE(p) { delete (p); (p) = NULL; }
#endif

// template for ogre rendering static box
template <class T, class TBase = CStatic, class TWinTraits = ATL::CControlWinTraits>
class ATL_NO_VTABLE COgreSceneBoxImpl : public ATL::CWindowImpl< T, TBase, TWinTraits>
{

public:
    DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())

    // constructor
    COgreSceneBoxImpl()
    : m_pRoot(NULL)
    , m_pRenderWindow(NULL)
    , m_pSceneManager(NULL)
    , m_pDefCamera(NULL)
    , m_pDefViewport(NULL)
    , m_bPause(FALSE)
    {

    }

    // destructor
    ~COgreSceneBoxImpl()
    {
        SAFE_DELETE(m_pRoot);        
    }

public:    
    BEGIN_MSG_MAP(COgreSceneBoxImpl)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)        
        MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
        MESSAGE_HANDLER(WM_PAINT, OnPaint)
        MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
        MESSAGE_HANDLER(WM_SIZE, OnSize)        
        MESSAGE_HANDLER(WM_QUIT, OnQuit)
    END_MSG_MAP()    

// Message Handler
public:
    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
    {
    }

    LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        return 1;   // no background needed
    }

    LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        T* pT = static_cast<T*>(this);
        if(wParam != NULL)
        {
            pT->Render((HDC)wParam);
        }
        else
        {
            CPaintDC dc(m_hWnd);
            pT->Render(dc.m_hDC);
        }
        return 0;
    }

    LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
    {        
    }

    LRESULT OnQuit(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
    {        
    }
    
// Overrideables methods(default implement)
public:
    // Load Plugin
    BOOL LoadPlugin()
    {    
    }

    // Select Render System
    BOOL SelectRenderSystem()
    {
    }

    // Create Render Window
    BOOL CreateRenderWindow()
    {
    }

    // Create Scene Manager
    BOOL CreateSceneManager()
    {
    }

    // Create Default Camera and viewport
    BOOL CreateDefaultCamera()
    {
    }

    // Render one Frame
    VOID Render(CDCHandle dc)
    {
    }

    // release resource
    VOID ReleaseOgre() { }

    // Load resources
    BOOL LoadResource()
    {
    }

// method must be overwrited
public:
    // Create Scene
    BOOL CreateScene()  { ATLASSERT(FALSE); return FALSE; }    

// public methods
public:
    // Init ogre
    BOOL Initialize()
    {            
    }    

    // overridden to provide proper initialization
    BOOL SubclassWindow(HWND hWnd)
    {
#if (_MSC_VER >= 1300)
        BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
#else //!(_MSC_VER >= 1300)
        typedef ATL::CWindowImpl< T, TBase, TWinTraits>   _baseClass;
        BOOL bRet = _baseClass::SubclassWindow(hWnd);
#endif //!(_MSC_VER >= 1300)
        if(bRet)
        {
            ModifyStyle(0, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
            Initialize();
        }
        return bRet;
    }

    // Pause render
    VOID Pause(BOOL bPause)
    {
        m_bPause = bPause;
    }

// data
public:
    Root*         m_pRoot;            // Ogre scene root
    SceneManager* m_pSceneManager;    // scene manager
    RenderWindow* m_pRenderWindow;    // render window
    Camera*      m_pDefCamera;       // default camera
    Viewport*     m_pDefViewport;     // default viewport
    BOOL          m_bPause;           // pause render

};

class COgreSceneBox : public COgreSceneBoxImpl<COgreSceneBox>
{
public:
    DECLARE_WND_SUPERCLASS(_T("WTL_OgreSceneBox"), GetWndClassName())
};

需要解释的唯一一点是控件的风格:WS_CLIPCHILEREN 是必须的,如果没有这个标志,绘制时3D图形会被场景本身附着的控件所覆盖。

        一篇写不开,下一节我会把控件所需要实现的消息响应函数挨个解释。

原创粉丝点击