osg窗口的创建(三)

前面两篇文章的分析只是讲述了窗口创建过程,但是里面还有部分细节并没有涉及,本文接着介绍这些内容,并详细讲述如何将osg嵌入到常用的窗口系统中:

  • 窗口的创建补充

在窗口创建过程中还有一个地方并没有讲述清楚,就是:为什么osg可以识别我想创建的窗口是Win32的?为什么它不会创建其他的窗口设备(如GraphicsWindowCocoa、GraphicsWindowX11)。

在osgViewer::Viewer的realize实现中,如果我们没有配置环境变量OSG_WINDOW和OSG_SCREEN,那么默认情况下osg会为我们创建一个全屏的窗口,它调用AcrossAllScreens中的configure函数进行配置,在配置函数configure代码中有下面这段代码:
            unsigned int width, height;
            wsi->getScreenResolution(si, width, height);

            osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(ds);
            traits->hostName = si.hostName;
            traits->displayNum = si.displayNum;
            traits->screenNum = si.screenNum;
            traits->screenNum = i;
            traits->x = 0;
            traits->y = 0;
            traits->width = width;
            traits->height = height;
            traits->windowDecoration = false;
            traits->doubleBuffer = true;
            traits->sharedContext = 0;

            osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());

            osg::ref_ptr<osg::Camera> camera = new osg::Camera;
            camera->setGraphicsContext(gc.get());
通过配置窗口的特性(traits),在调用osg::GraphicsContext中的createGraphicsContext函数来创建设备上下文,看该函数的详细实现:
GraphicsContext* GraphicsContext::createGraphicsContext(Traits* traits)
{
    ref_ptr<GraphicsContext::WindowingSystemInterface> &wsref = windowingSystemInterfaceRef();
    if ( wsref.valid())
    {
        // catch any undefined values.
        if (traits) traits->setUndefinedScreenDetailsToDefaultScreen();

        return wsref->createGraphicsContext(traits);
    }
    else
        return 0;
}
可以看到该函数调用了
windowingSystemInterfaceRef
来获取 WindowingSystemInterface,这个WindowingSystemInterface就是与平台相关的一些API调用,在GraphicsWindowWin32中实现了它的派生类Win32WindowingSystem,同样其他的窗口系统中也有类似的代码。该类也实现了一个与GraphicsContext一样的方法createGraphicsContext,这个函数就是真正创建窗口渲染上下文的函数,它的实现如下:
    if (traits->pbuffer)
    {
        osg::ref_ptr<osgViewer::PixelBufferWin32> pbuffer = new PixelBufferWin32(traits);
        if (pbuffer->valid()) return pbuffer.release();
        else return 0;
    }
    else
    {
        registerWindowClasses();

        osg::ref_ptr<osgViewer::GraphicsWindowWin32> window = new GraphicsWindowWin32(traits);
        if (window->valid()) return window.release();
        else return 0;
    }
在 registerWindowClasses中我们可以看到许多Win32程序设计熟悉的内容:
    if (_windowClassesRegistered) return;

    //
    // Register the window classes used by OSG GraphicsWindowWin32 instances
    //

    std::ostringstream str;
    str << "OSG Graphics Window for Win32 [" << ::GetCurrentProcessId() << "]";

    osgGraphicsWindowWithCursorClass    = str.str() + "{ with cursor }";
    osgGraphicsWindowWithoutCursorClass = str.str() + "{ without cursor }";

    WNDCLASSEX wc;

    HINSTANCE hinst = ::GetModuleHandle(NULL);

    //
    // First class: class for OSG Graphics Window with a cursor enabled
    //

    wc.cbSize        = sizeof(wc);
    wc.style         = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc   = WindowProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hinst;
    wc.hIcon         = ::LoadIcon(hinst, "OSG_ICON");
    wc.hCursor       = ::LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = 0;
    wc.lpszClassName = osgGraphicsWindowWithCursorClass.c_str();
    wc.hIconSm       = NULL;

    if (::RegisterClassEx(&wc)==0)
    {
        unsigned int lastError = ::GetLastError();
        if (lastError!=ERROR_CLASS_ALREADY_EXISTS)
        {
            reportError("Win32WindowingSystem::registerWindowClasses() - Unable to register first window class", lastError);
            return;
        }
    }
也就是说 GraphicsContext的创建工作实际是在WindowingSystemInterface中完成的,GraphicsContext的createGraphicsContext只是调用了WindowingSystemInterface中的同名方法注册了不同平台的窗口。它们的关系如下图所示:




那么为什么是Win32的窗口了,这是因为在编译OSG的过程中我们已经选择了一个窗口系统进行编译(在cmake中可以查看),比如我是在Win32下进行编译的,那么GraphicsWindowWin32.cpp的代码生成到了我的osgViewer.dll中,在GraphicsWindowWin32中有如下一段代码:

struct RegisterWindowingSystemInterfaceProxy
{
    RegisterWindowingSystemInterfaceProxy()
    {
        osg::GraphicsContext::setWindowingSystemInterface(Win32WindowingSystem::getInterface());
    }

    ~RegisterWindowingSystemInterfaceProxy()
    {
        if (osg::Referenced::getDeleteHandler())
        {
            osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
            osg::Referenced::getDeleteHandler()->flushAll();
        }

        osg::GraphicsContext::setWindowingSystemInterface(0);
    }
};

static RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy;

它声明了一个全局的变量,使用了
setWindowingSystemInterface
声明是Win32的窗口系统,这个语句的调用作了下面的事情:
void GraphicsContext::setWindowingSystemInterface(WindowingSystemInterface* callback)
{
    ref_ptr<GraphicsContext::WindowingSystemInterface> &wsref = windowingSystemInterfaceRef();
    wsref = callback;
    OSG_INFO<<"GraphicsContext::setWindowingSystemInterface() "<<wsref.get()<<"\t"<<&wsref<<std::endl;
}
这个windowingSystemInterfaceRef实际上引用的是一个全局的静态变量

static ref_ptr<GraphicsContext::WindowingSystemInterface> &windowingSystemInterfaceRef()
{
    static ref_ptr<GraphicsContext::WindowingSystemInterface> s_WindowingSystemInterface;
    return s_WindowingSystemInterface;
}
当在WindowingSystemInterface这个函数在
createGraphicsContext
的时候,它会获取到这个已经被初始化的静态变量,这个变量的类型就是Win32窗口类型的,所以最终产生的窗口是Win32的。

总结一下就是: GraphicsContext这个最上层的抽象类中保留了一个用来标示窗口类型的指针,而这个指针在GraphicsWindowWin32.cpp中被赋值了(这个赋值过程由于是初始化一个全局的变量,它甚至在进入程序main函数之前就完成了),然后窗口在创建过程中调用createGraphicsWindow获取了这个指针,并依据这个指针的信息(Win32类型的窗口信息)创建了窗口(当然就是Win32的窗口)。

  • win32中的使用

我们可以参考OSG中的代码将窗口嵌入到Win32的程序中:

#include <windows.h>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/api/win32/GraphicsWindowWin32>
#include <osgUtil/Optimizer>
#include <osgGA/TrackballManipulator>
#include <osgDB/ReadFile>
#include <process.h>

osg::ref_ptr<osgViewer::Viewer> viewer;
osg::observer_ptr<osgViewer::GraphicsWindow> window;
osg::ref_ptr<osg::Group> root;
osg::ref_ptr<osg::Node> loadedModel;
osgUtil::Optimizer optimizer;
osg::ref_ptr<osg::GraphicsContext::Traits> traits;
osg::ref_ptr<osg::Referenced> windata;
osg::GraphicsContext* gc;
osg::ref_ptr<osg::Camera> camera;
bool renderok;


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("HelloWin");
	HWND hwnd;
	MSG msg;
	WNDCLASS wndclass;
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;
	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
		return 0;
	}
	hwnd = CreateWindow(szAppName, // window class name
		TEXT("The Hello Program"), // window caption
		WS_OVERLAPPEDWINDOW, // window style
		100, // initial x position
		100, // initial y position
		800, // initial x size
		600, // initial y size
		NULL, // parent window handle
		NULL, // window menu handle
		hInstance, // program instance handle
		NULL); // creation parameters
	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return (int)msg.wParam;
}


void render(void* ptr)
{
	while (!viewer->done())
	{
		viewer->frame();
	}
	renderok = true;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CREATE:
		root = new osg::Group;
		loadedModel = osgDB::readNodeFile("glider.osg");
		if (!loadedModel)
			return 1;
		optimizer.optimize(loadedModel.get());
		optimizer.reset();
		root->addChild(loadedModel.get());
		traits = new osg::GraphicsContext::Traits;
		windata = new osgViewer::GraphicsWindowWin32::WindowData(hwnd);
		traits->x = 0;
		traits->y = 0;
		traits->width = 800;
		traits->height = 600;
		traits->windowDecoration = false;
		traits->doubleBuffer = true;
		traits->sharedContext = 0;
		traits->inheritedWindowData = windata;
		traits->setInheritedWindowPixelFormat = true;
		gc = osg::GraphicsContext::createGraphicsContext(traits.get());
		camera = new osg::Camera;
		camera->setGraphicsContext(gc);
		camera->setViewport(new osg::Viewport(traits->x, traits->y, traits->width, traits->height));
		viewer = new osgViewer::Viewer;
		viewer->addSlave(camera.get());
		viewer->setSceneData(root.get());
		viewer->setCameraManipulator(new osgGA::TrackballManipulator);
		viewer->addEventHandler(new osgViewer::StatsHandler);
		viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
		viewer->realize();
		renderok = false;
		_beginthread(render, 0, NULL);
		return 0;
	case WM_PAINT:
		return 0;
	case WM_DESTROY:
		viewer->setDone(true);
		while (!renderok)
		{
			Sleep(10);
		}
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
在这个Win32程序中,所有的事件我们可以在WM_的事件中进行处理。

事实上我们可以直接使用osgViewer::Viewer来创建窗口,让整个创建过程在osg中完成(也就是说在上面的Win32中的注册窗口类代码,事件处理等有osg内部实现),当我们需要窗口的时候,我们可以从GraphicsContext(Win32中是GraphicsWindowWin32)中获取到窗口的句柄(_hwnd),窗口的DC,以及OpenGL的渲染上下文HGLRC,然后对得到的句柄或其他资源进行操作(例如添加菜单、ToolBar栏等),窗口的大小如果不设置默认是全屏的,可以使用SetUpViewInWindow来设置大小和窗口的初始位置。


osg窗口创建的文章参考了网络上的一些资料,包括:

1. 王锐《最长的一帧》


  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值