Irrlicht学习备忘录——14 Win32Window

14 Win32Window

    官方代码($sdk)\examples\14.Win32Window

Irrlicht学习备忘录——14 Win32Window_第1张图片

    这个例子演示了如何在win32窗口里运行irr。用过windows api或MFC的都应该知道窗口句柄这东西,通过它,就能让irr在自己指定的窗口里运行,而不是irr自己创建的窗口里运行。开发经验丰富人一看到这个官方例子,应该就知道如何让irr运行到Qt和.Net的窗口里了。这个例子是用windows api写的,看懂执行流程要比用框架的简单的多,但对windows的消息驱动的工作流程不清楚的,看这例子就有点困难了,需要先去了解下如何用windows api写一个HelloWorld。
    例子里一开头定义了两个全局的句柄,一个按钮的,另一个窗口的。
//按钮句柄
HWND hOKButton;
//窗口句柄
HWND hWnd;
    接下来是消息处理的回调函数。窗口收到属于该窗口的消息时,会自动调用它的回调函数对消息进行处理。
    CustomWndProc函数的样式是固定的,不能随意增减或改变参数,它将以函数指针的形式被窗口调用。参数HWND窗口句柄;UINT被传过来的消息ID,通过这ID可以知道具体是什么类型的消息;WPARAM和LPARAM附加到消息上的数据,它们可以是直接的数据,也可以是指针,具体需根据消息类型来确定,和API里面的MSG结构一样。
static LRESULT CALLBACK CustomWndProc(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    //处理窗口的命令
    case WM_COMMAND:
    {
        HWND hwndCtl = (HWND)lParam;
        int code = HIWORD(wParam);
        //按钮发出的消息
        if (hwndCtl == hOKButton)
        {
            //销毁窗口
            DestroyWindow(hWnd);
            //发送一个结束消息,告诉系统线程要结束了
            PostQuitMessage(0);
            return 0;
        }
    }
    break;
    //窗口销毁
    case WM_DESTROY:
    //发送一个结束消息,告诉系统线程要结束了
    PostQuitMessage(0);
    return 0;
    }
    //使用系统默认的消息处理函数
    return DefWindowProc(hWnd, message, wParam, lParam);
}
    这个例子里的消息处理函数功能很简单,它只处理了窗口收到的按钮点击消息和退出消息。如果需要处理其他的消息,增加相应的case就可以了。下面是主函数了,这次例子里主函数的代码跟前面的例子比,可以说完全不一样了。
int main()
{
    //这里跟以往的例子一样,命令行上让用户选择irr驱动方式
    video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (driverType==video::EDT_COUNT)
        return 1;
    //选择渲染窗口,这里有三种方式让用户选择。a、带有按钮的窗口,使用创建参数;b、带有按钮的窗口,使用beginScene;c、irr自有窗口,irr默认使用方式
    printf("Select the render window (some dead window may exist too):\n"\
        " (a) Window with button (via CreationParam)\n"\
        " (b) Window with button (via beginScene)\n"\
        " (c) Own Irrlicht window (default behavior)\n"\
        " (otherKey) exit\n\n");
    char key;
    std::cin >> key;
    if (key != 'a' && key != 'b' && key != 'c')
        return 1;
    //应用程序实例句柄
    HINSTANCE hInstance = 0;
    //注册窗口类
    //irr注册类名
    const char* Win32ClassName = "CIrrlichtWindowsTestDialog";
    //填写窗口类的数据
    WNDCLASSEX wcex;
    //窗口类的结构体的大小
    wcex.cbSize            = sizeof(WNDCLASSEX);
    //定义窗口风格
    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    //窗口使用的消息处理回调函数,就是代码一开头写的那个函数
    wcex.lpfnWndProc    = (WNDPROC)CustomWndProc;
    //给类而外预留的空间默认设为0。我还没用到过,暂不明白有何用途。
    wcex.cbClsExtra        = 0;
    //跟上面那个一样
    wcex.cbWndExtra        = DLGWINDOWEXTRA;
    //该窗口的应用程序实例句柄
    wcex.hInstance        = hInstance;
    //该窗口的图标
    wcex.hIcon            = NULL;
    //该窗口的光标形式
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    //该窗口的背景画刷
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW);
    //该窗口使用的菜单
    wcex.lpszMenuName    = 0;
    //该类的名字
    wcex.lpszClassName    = Win32ClassName;
    //该窗口任务栏图标
    wcex.hIconSm        = 0;
    //注册窗口类
    RegisterClassEx(&wcex);
    //创建窗口使用的窗口样式
    DWORD style = WS_SYSMENU | WS_BORDER | WS_CAPTION |    WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX;
    //窗口宽度
    int windowWidth = 440;
    //窗口高度
    int windowHeight = 380;
    //创建窗口
    hWnd = CreateWindow( Win32ClassName, "Irrlicht Win32 window example",style, 100, 100, windowWidth, windowHeight,NULL, NULL, hInstance, NULL);
    //获取窗口客户区矩形
    RECT clientRect;
    GetClientRect(hWnd, &clientRect);
    windowWidth = clientRect.right;
    windowHeight = clientRect.bottom;
    //创建OK按钮
    hOKButton = CreateWindow("BUTTON", "OK - Close", WS_CHILD | WS_VISIBLE | BS_TEXT,windowWidth - 160, windowHeight - 40, 150, 30, hWnd, NULL, hInstance, NULL);
    // 在窗口上创建静态文本
    CreateWindow("STATIC", "This is Irrlicht running inside a standard Win32 window.\n"\
        "Also mixing with MFC and .NET Windows.Forms is possible.",
        WS_CHILD | WS_VISIBLE, 20, 20, 400, 40, hWnd, NULL, hInstance, NULL);
    //创建运行irr的窗口
    HWND hIrrlichtWindow = CreateWindow("BUTTON", "",WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,50, 80, 320, 220, hWnd, NULL, hInstance, NULL);
    SExposedVideoData结构的定义见SExposedVideoData.h,它是用来保存一个描述操作系统和驱动的数据结构。前面的窗口选项按钮就在这里起作用,如果irr使用的是OpenGL驱动,后面还需要对它设置。
    video::SExposedVideoData videodata((key=='b')?hIrrlichtWindow:0);
    //现在已经有了一些窗口,使用Irrlicht的createDeviceEx()函数,就可以将irr应用到窗口中。它只需要一个窗口句柄,把它作为窗口ID参数,就可以跟平时一样使用irr引擎。
    // 创建一个irr设备到按钮窗口中
    SIrrlichtCreationParameters结构体,createDeviceEx()使用的创建参数结构。详见SIrrlichtCreationParameters.h文件
    irr::SIrrlichtCreationParameters param;
    //设置irr的驱动类型
    param.DriverType = driverType;
    //根据选择的窗口类型,将相应窗口句柄设为窗口ID参数
    if (key=='a')
        param.WindowId = reinterpret_cast(hIrrlichtWindow);
    //创建irr设备
    irr::IrrlichtDevice* device = irr::createDeviceEx(param);
    if (!device)
        return 1;
    irr::scene::ISceneManager* smgr = device->getSceneManager();
    video::IVideoDriver* driver = device->getVideoDriver();
    //如果使用的是OpenGL驱动
    if (driverType==video::EDT_OPENGL)
    {
        //获取窗口的DC
        HDC HDc=GetDC(hIrrlichtWindow);
        //设置OpenGL需要的像素格式
        PIXELFORMATDESCRIPTOR pfd={0};
        pfd.nSize=sizeof(PIXELFORMATDESCRIPTOR);
        int pf = GetPixelFormat(HDc);
        DescribePixelFormat(HDc, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
        pfd.dwFlags |= PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
        pfd.cDepthBits=16;
        pf = ChoosePixelFormat(HDc, &pfd);
        SetPixelFormat(HDc, pf, &pfd);
        //创建OpenGL的RC并将DC和RC加入到SExposedVideoData结构
        videodata.OpenGLWin32.HDc = HDc;
        videodata.OpenGLWin32.HRc=wglCreateContext(HDc);
        //开启OpenGL的资源共享,这样就可以让两个RC使用相同的纹理等资源。还好有近两年自己一个人鼓捣OpenGL做引擎,看着部分没什么不懂的。
        wglShareLists((HGLRC)driver->getExposedVideoData().OpenGLWin32.HRc, (HGLRC)videodata.OpenGLWin32.HRc);
    }
    //创建简单的3D场景,这里和以前的例子没什么不同
    scene::ICameraSceneNode* cam = smgr->addCameraSceneNode();
    cam->setTarget(core::vector3df(0,0,0));
    scene::ISceneNodeAnimator* anim = smgr->createFlyCircleAnimator(core::vector3df(0,15,0), 30.0f);
    cam->addAnimator(anim);
    anim->drop();
    scene::ISceneNode* cube = smgr->addCubeSceneNode(20);
    cube->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
    cube->setMaterialTexture(1, driver->getTexture("../../media/water.jpg"));
    cube->setMaterialFlag( video::EMF_LIGHTING, false );
    cube->setMaterialType( video::EMT_REFLECTION_2_LAYER );
    smgr->addSkyBoxSceneNode(
        driver->getTexture("../../media/irrlicht2_up.jpg"),
        driver->getTexture("../../media/irrlicht2_dn.jpg"),
        driver->getTexture("../../media/irrlicht2_lf.jpg"),
        driver->getTexture("../../media/irrlicht2_rt.jpg"),
        driver->getTexture("../../media/irrlicht2_ft.jpg"),
        driver->getTexture("../../media/irrlicht2_bk.jpg"));
    //又回到windows api程序了,显示窗口
    ShowWindow(hWnd , SW_SHOW);
    //刷新窗口
    UpdateWindow(hWnd);
    //处理消息队列
    现在唯一缺少的就是前面所有的例子都出现过的绘图循环,那段一帧画面一循环的帧循环。可以简单的使用GetMessage,DispatchMessage等消息循环。也能调用Device->Run(),Irr引擎将在内部获取Windows的消息分发了。如果想使用自己的消息循环,就不需要调用Device->Run()了,但是rr就无法获取Windows分发的用户输入信息,那就只能使用DirectInput或其他方式了。
    //通常使用的irr绘图循环
    while (device->run())
    {
        driver->beginScene(true, true, 0, videodata);
        smgr->drawAll();
        driver->endScene();
    }
    //如果没有调用Device->run(),就必须这样自己获取消息了。注意,这两段while不能同时出现在代码里。
    MSG msg;
    while (true)
    {
        //获取窗口消息
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            //转换加速键消息给窗口回调函数
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            //收到退出消息,停止循环
            if (msg.message == WM_QUIT)
                break;
        }
        //irr获取虚拟时间
        device->getTimer()->tick();
        //绘制图像,这里的beginScene也有些不同哦,它对应着开始时的窗口选项。
        driver->beginScene(true, true, 0, (key=='c')?hIrrlichtWindow:0);
        smgr->drawAll();
        driver->endScene();
    }
    //程序结束,关闭和释放irr设备
    device->closeDevice();
    device->drop();
    return 0;
}

你可能感兴趣的:(irrlicht,irrlicht,游戏引擎,学习,游戏编程,3d引擎)