Wm5::WindowApplication类 说说OpenGL应用的一个渲染框架

    先说说,Wm5::WindowApplication类要实现一个什么功能呢?要实现一个窗口操作系统中,提供一个供OpenGL渲染的环境,可以提供鼠标键盘事件响应处理、获取字体大小、鼠标位置控制,帧速监测和显示,渲染场景等功能。总体说有点类似GLUT等框架的功能。


    与GLUT不同,作者做了这样一个设计愿景,他希望Application应用类是彻底面向对象的,不需要用户再写主函数和把函数指针注册,每一帧通过回调函数处理事件和渲染。那样是不够oo(object oriented)的。这样在设计上就要用到些技巧。

    首先我们要找入口函数,C++中入口函数一定是main方法,但是main方法中的具体内容是在库中已经写好的,如果应用程序工程加载了库,main方法依然是入口函数。下面是main方法(部分):

int main (int numArguments, char* arguments[])
{
    ...

    InitTerm::ExecuteInitializers();

    int exitCode = Application::Run(numArguments, arguments);

    InitTerm::ExecuteTerminators();

    ...

    return exitCode;
}

    完整的代码可以去源码“Wm5Application.cpp”文件中去详查。省略掉的工作大体也是通过一些单例类实现的环境配置如根目录,各资源目录,应用程序内存和参数列表处理等。

    这里除了InitTerm单例类执行的初始化和终止操作外,最主要就是Application::Run方法。你可以把它当做一个仿真循环。这里需要注意的是,这个static的Run方法来自Application(WindowApplication的基类),而真正的内容一定是在WindowApplication类中的,这里又做了什么技巧?

    这里有理由相信一定是与InitTerm::ExecuteInitializers方法有关系的。这里我们假设它做了初始化工作,但是做了什么初始化一定是要赋予它的。而main方法已经写死了,无法灵活的控制将什么在此初始化。那么又有什么可以先于入口函数main得到执行呢。对,全局的变量。

    这里贴一下构建起这个架构的所有定义:

#define WM5_WINDOW_APPLICATION(classname) \
WM5_IMPLEMENT_INITIALIZE(classname); \
WM5_IMPLEMENT_TERMINATE(classname); \
\
void classname::Initialize () \
{ \
    Application::Run = &WindowApplication::Run; \
    TheApplication = new0 classname(); \
} \
\
void classname::Terminate () \
{ \
    delete0(TheApplication); \
}

#define WM5_DECLARE_INITIALIZE \
public: \
    static bool RegisterInitialize (); \
    static void Initialize (); \
private: \
    static bool msInitializeRegistered
//----------------------------------------------------------------------------
#define WM5_IMPLEMENT_INITIALIZE(classname) \
bool classname::msInitializeRegistered = false; \
bool classname::RegisterInitialize () \
{ \
    if (!msInitializeRegistered) \
    { \
        InitTerm::AddInitializer(classname::Initialize); \
        msInitializeRegistered = true; \
    } \
    return msInitializeRegistered; \
}
//----------------------------------------------------------------------------
#define WM5_REGISTER_INITIALIZE(classname) \
static bool gsInitializeRegistered_##classname = \
    classname::RegisterInitialize ()
//----------------------------------------------------------------------------
#define WM5_DECLARE_TERMINATE \
public: \
    static bool RegisterTerminate (); \
    static void Terminate (); \
private: \
    static bool msTerminateRegistered
//----------------------------------------------------------------------------
#define WM5_IMPLEMENT_TERMINATE(classname) \
bool classname::msTerminateRegistered = false; \
bool classname::RegisterTerminate () \
{ \
    if (!msTerminateRegistered) \
    { \
        InitTerm::AddTerminator(classname::Terminate); \
        msTerminateRegistered = true; \
    } \
    return msTerminateRegistered; \
}
//----------------------------------------------------------------------------
#define WM5_REGISTER_TERMINATE(classname) \
static bool gsTerminateRegistered_##classname = \
    classname::RegisterTerminate ()

    这里全是宏定义,具体仍然可以去源文件中查看。这种通过宏定义构造的方法巧妙而常见,比如著名的MFC和Qt等等。其目的是让构建一个应用程序更方便,因为其实现很通用,只要按照这样的步骤即可为类赋予了已为其设计好的功能。

    这里只捋顺一下整体设计,具体实现,可自行理解。


    到此我们知道真正的C++入口函数main实际通过WindowApplication::Run方法进入仿真循环。这里要说下WindowApplication类究竟做了哪两方面工作。

    开题例举了下WindowApplication类的功能,这里要给它功能分个类:PlatformDependent和PlatformIndependent。因为我们知道对于OpenGL应用窗口的渲染环境是平台相关的,而真正的渲染和缓冲区操作是平台无关的。所以考虑到作为一个跨平台应用,头文件是抽象的,而具体实现分别写在两个cpp中,其中如果平台更换,只要把平台相关的cpp替换即可。

    至此,框架分析完毕,因为在Windows下的调用win32创建窗口、获得hwnd然后创建hDc和hRc、并make current,这个过程是如此的通用固定,也不具体分析了。

    通过实际例子的接触,相信会发现wild magic5的这个轻量应用架构作为在封装性和灵活性上有很好的权衡,而且对我们的阅读和写作也有很强的指导意义。

你可能感兴趣的:(wildmagic5)