这个世界或许只是一扇窗……
上一篇我们介绍了Root的创建,和加载插件的方法。没有涉及到Direct3D的内容,那么这篇我准备介绍Ogre是如何初始化Direct3D的,如何初始化一个窗口。就如同我们在学习Direct3D进入实践的第一课:开启一面窗,来绘制整个世界。
首先在开始之前来温习一下Direct3D的初始化步骤,下面引用《DirectX_9.0_3D游戏开发编程基础》(红龙书)46页:
下面几点说明怎样初始化Direct3D。根据下边的步骤你能初始化Direct3D:
1.获得一个IDirect3D9接口指针。这个接口用于获得物理设备的信息和创建一个
IDirect3DDevice9接口,它是一个代表我们显示3D图形的物理设备的C++对象。
2.检查设备能力(D3DCAPS9),搞清楚主显卡是否支持硬件顶点处理。我们需要知道假如它能支持,我们就能创建IDirect3DDevice9接口。
3.初始化一个D3DPRESENT_PARAMETERS结构实例,这个结构包含了许多数据成员允许我们指定将要创建的IDirect3DDevice9接口的特性。
4.创建一个基于已经初始化好的D3DPRESENT_PARAMETERS结构的IDirect3DDevice9对
象。它是一个代表我们显示3D图形的物理设备的C++对象。
我们来看看Ogre是如何实现上面4点的。
接着上篇,找到plugin = OGRE_NEW D3D9Plugin()这一句,可以跟踪到D3D9Plugin::install(),这个方法中的mRenderSystem = OGRE_NEW D3D9RenderSystem( hInst );就是初始化DirectX设备的地方。再次跟踪到D3D9RenderSystem的构造函数中,找到下面代码:
if( NULL == (mD3D = Direct3DCreate9(D3D_SDK_VERSION)) )
Direct3DCreate9(D3D_SDK_VERSION)出现了,初始化了一个IDirect3D9的指针。第一点实现完成。
对于2,3,4点,Ogre已经对其进行了封装,要直接找到不是很容易,我们来一步步跟踪代码找到实现2,3,4点的地方。
代码定位到ExampleApplication.h中的mWindow = mRoot->initialise(true);看注释知道这一句是创建一个默认的渲染窗口。
跟踪到mRoot->initialise中,定位到这一句代码
mAutoWindow = mActiveRenderer->_initialise(autoCreateWindow, windowTitle);
这里我选择的是D3D,所以mActiveRenderer是D3D9RenderSystem类型,如果不知道它是怎么来的,可以看看上一篇的介绍(说过这个玩意儿很重要了……)。
好,多态,就会调用下面的方法:
RenderWindow* D3D9RenderSystem::_initialise( bool autoCreateWindow, const String& windowTitle )
在这个方法中,好像出现了D3DPRESENT_PARAMETERS的影子,是不是这个呢?
NameValuePairList miscParams;
miscParams["colourDepth"] = StringConverter::toString(videoMode->getColourDepth());
miscParams["FSAA"] = StringConverter::toString(mFSAASamples);
miscParams["FSAAHint"] = mFSAAHint;
miscParams["vsync"] = StringConverter::toString(mVSync);
miscParams["vsyncInterval"] = StringConverter::toString(mVSyncInterval);
miscParams["useNVPerfHUD"] = StringConverter::toString(mUseNVPerfHUD);
miscParams["gamma"] = StringConverter::toString(hwGamma);
miscParams["monitorIndex"] = StringConverter::toString(static_cast<int>(mActiveD3DDriver->getAdapterNumber()));
再往下看,跟踪到:
autoWindow = _createRenderWindow( windowTitle, width, height,
fullScreen, &miscParams );
进入_createRenderWindow方法,首先定位到
D3D9RenderWindow* renderWindow = OGRE_NEW D3D9RenderWindow(mhInstance);
renderWindow->create(name, width, height, fullScreen, miscParams);
参数mhInstance为GetModuleHandle( "RenderSystem_Direct3D9.dll" ),不多解释。
在上面的话执行完之后,窗口被创建出来了,这个renderWindow将被返回出去。容易看到miscParams被当做了参数继续它的征程,好的,跟踪进去看看:
void D3D9RenderWindow::create(const String& name, unsigned int width, unsigned int height, bool fullScreen, const NameValuePairList *miscParams)
先看这个方法的实现,来看看D3D9RenderWindow的保护成员:
protected:
HINSTANCE mInstance; // Process instance
D3D9Device* mDevice; // D3D9 device wrapper class.
bool mDeviceValid; // Device was validation succeeded.
HWND mHWnd; // Win32 Window handle
bool mIsExternal; // window not created by Ogre
bool mClosed; // Is this window destroyed.
bool mHidden; // True if this is hidden render window.
bool mSwitchingFullscreen; // Are we switching from fullscreen to windowed or vice versa
D3DMULTISAMPLE_TYPE mFSAAType; // AA type.
DWORD mFSAAQuality; // AA quality.
UINT mDisplayFrequency; // Display frequency.
bool mVSync; // Use vertical sync or not.
unsigned int mVSyncInterval; // The vsync interval.
bool mUseNVPerfHUD; // Use NV Perf HUD.
DWORD mWindowedWinStyle; // Windowed mode window style flags.
DWORD mFullscreenWinStyle; // Fullscreen mode window style flags.
unsigned int mDesiredWidth; // Desired width after resizing
unsigned int mDesiredHeight; // Desired height after resizing
现在可以告诉大家的是上面的成员变量对应着D3DPRESENT_PARAMETERS的一些项,D3D9RenderWindow在执行create是会对这些成员变量赋值,然后赋给IDirect3DDevice9对象。
但是到现在我们还未看到IDirect3DDevice9对象,上面说的第2点,设备检测到底在哪里啊,还有搞了这么一大圈,在搞啥啊?还有啥时候给D3DPRESENT_PARAMETERS赋值?IDirect3DDevice9对象在哪里产生的啊?……
在D3D9RenderWindow::create(const String& name, unsigned int width, unsigned int height,bool fullScreen, const NameValuePairList *miscParams)中,干了2件大事:1.成员变量赋值,就是将miscParams赋值给成员变量。2.创建了窗口,定位到这一句:
mHWnd = CreateWindowEx(dwStyleEx, "OgreD3D9Wnd", title.c_str(), getWindowStyle(fullScreen),mLeft, mTop, winWidth, winHeight, parentHWnd, 0, hInst, this);
熟悉windows编程的码农们该很happy吧。窗口句柄出现了------- mHWnd,窗口就这么创建出来了,请记住这个句柄,后面要用的。
好,跳出去,再跟踪,想想上面的问题,下面来一个个回答。
回到RenderWindow* D3D9RenderSystem::_createRenderWindow(const String &name, unsigned int width, unsigned int height, bool fullScreen,const NameValuePairList *miscParams)中,定位到mDeviceManager->linkRenderWindow(renderWindow), 这里出现了新的对象D3D9DeviceManager,它是个管理器,管理着D3D9Device,哦!D3D9Device,这个东西是个重点,赶紧看看它的成员变量:
D3D9DeviceManager* mDeviceManager; // The manager of this device instance.
IDirect3DDevice9* mDevice; // Will hold the device interface.
UINT mAdapterNumber; // The adapter that this device belongs to.
HMONITOR mMonitor; // The monitor that this device belongs to.
D3DDEVTYPE mDeviceType; // Device type.
static HWND msSharedFocusWindow; // The shared focus window in case of multiple full screen render windows.
HWND mFocusWindow; // The focus window this device attached to.
DWORD mBehaviorFlags; // The behavior of this device.
D3DPRESENT_PARAMETERS* mPresentationParams; // Presentation parameters which the device was created with. May be
// an array of presentation parameters in case of multi-head device.
UINT mPresentationParamsCount; // Number of presentation parameters elements.
D3DCAPS9 mD3D9DeviceCaps; // Device caps.
bool mD3D9DeviceCapsValid; // True if device caps initialized.
D3DDEVICE_CREATION_PARAMETERS mCreationParams; // Creation parameters.
uint mLastPresentFrame; // Last frame that this device present method called.
bool mDeviceLost; // True if device entered lost state.
奶奶的! IDirect3DDevice9,D3DPRESENT_PARAMETERS,D3DCAPS9 都在这个类中啊!那么D3D9Device的作用是什么就知道了吧,它是对D3D设备的一个封装类。
来继续我们的征程,跟踪到void D3D9DeviceManager::linkRenderWindow(D3D9RenderWindow* renderWindow)方法,看到了D3D9Device* renderDevice,不过只是个指针,没有实例化,定位到renderDevice = selectDevice(renderWindow, renderWindowsGroup); 进入该方法,靠!好长一坨代码,只看这最后几句:
// No matching device found -> create new one.
if (renderDevice == NULL)
{
renderDevice = OGRE_NEW D3D9Device(this, nAdapterOrdinal, direct3D9->GetAdapterMonitor(nAdapterOrdinal), devType, extraFlags);
mRenderDevices.push_back(renderDevice);
if (mActiveDevice == NULL)
setActiveDevice(renderDevice);
}
D3D9Device对象是创建出来了,但是它的成员变量是在哪里创建的呢? 别急,我们先跳出这恶心的一坨代码。
注意这部分代码:
// Link the windows group to the new device.
for (uint i = 0; i < renderWindowsGroup.size(); ++i)
{
D3D9RenderWindow* currWindow = renderWindowsGroup[i];
currWindow->setDevice(renderDevice);
renderDevice->attachRenderWindow(currWindow);
renderDevice->setAdapterOrdinalIndex(currWindow, i);
}
我们只是创建一个渲染窗口,有时需要创建多个渲染窗口,上面的for循环就是干这个事的,这里不讲多窗口的事。上面代码可以读出的重要信息是:窗口对象D3D9RenderWindow和设备对象D3D9Device是你中有我,我中有你。renderDevice->attachRenderWindow(currWindow)方法中,将窗口对象和相关设备资源加入了一个哈希表:RenderWindowToResourcesMap,后面还需要用到这个哈希表,关于这个表大家可以看代码,不多介绍。
注意,重点来了。
renderDevice->acquire();
进入该方法,跟踪到void D3D9Device::updatePresentationParameters(),定位到renderWindow->buildPresentParameters(&renderWindowResources->presentParameters),执行这一句后就将 D3D9RenderWindow中的成员变量赋值给renderWindowResources->presentParameters,renderWindowResources上面已经简单介绍过了。跳出该方法,定位到 mPresentationParams[renderWindowResources->presentParametersIndex] = renderWindowResources->presentParameters;
mPresentationParams是个D3DPRESENT_PARAMETERS指针,这里当做数组来用。它的初始化是:
mPresentationParams = OGRE_ALLOC_T(D3DPRESENT_PARAMETERS, mMapRenderWindowToResources.size(), MEMCATEGORY_RENDERSYS);
因为Ogre是支持多窗口渲染的,所以要这样搞。记着这个mPresentationParams,它目前有一个数组成员,值为renderWindowResources->presentParameters。跳出方法updatePresentationParameters(),定位到createD3D9Device(),不用说,这个就是创建D3D设备的地方。
看下面代码:
hr = pD3D9->CreateDevice(mAdapterNumber, mDeviceType, mFocusWindow,
mBehaviorFlags, mPresentationParams, &mDevice);
mFocusWindow就是D3D9RenderWindow创建出来的mHWnd,不清楚的这个句柄怎么来的,再看看上面的介绍吧。
继续往下看代码,看到很多if (FAILED(hr)),这个就是在检测硬件设备,也就是第2点,一个个的检测,如果你的机子是老掉牙的,那么会抛个异常给你,告诉你,你的电脑玩不了D3D设备的OGRE。
好了第4点也找到了,第3点也介绍了。
下面付个图看看调用到createD3D9Device的堆栈,如图:
经过上面的步骤,我们明白了Ogre是如何创建渲染窗口的,是如何初始化D3D设备的,有点复杂,搞懂了还是觉得很有收获的。先写到这里,下一篇介绍一下渲染循环: mRoot->startRendering();