天天看點

Irrlicht 0.1引擎源碼分析與研究(二)

裝置的建立

同時也可以看到,使用Irrlicht實際上是很簡單的,它幫助我們完成了大多數的功能。下面對這段代碼這段代碼的内部實作進行詳細剖析(僅針對Windows平台,下面未說明都指Windows平台),首先建立裝置對象:

//裝置指針

IrrlichtDevice *pDevice =

createDevice(video::DT_SOFTWARE, dimension2d<s32>(800,600),16,false,0);

//0.1版本的函數的參數個數與現在版本1.7.2有差別DT_SOFTWARE在新版中變為EDT_SOFTWARE,參數的個數也不一樣。

createDevice函數的定義在CIrrDeviceWin32.cpp檔案裡面,如下所示:

IRRLICHT_API IrrlichtDevice* createDevice(video::EDriverType driverType,

const core::dimension2d<s32>& windowSize,

u32 bits, bool fullscreen, IEventReceiver* res)

{

return new CIrrDeviceWin32(driverType, windowSize, bits, fullscreen, res);

}

再來看看IRRLICHT_API是個什麼東東?在該函數前有如下語句:

#ifdef IRRLICHT_EXPORTS

#define IRRLICHT_API __declspec(dllexport)

#else

#define IRRLICHT_API __declspec(dllimport)

#endif

原來IRRLICHT_API是DLL的導出函數辨別符,所有Irrlicht.dll提供給外部調用的函數前都要用此修飾符。整個Irrlicht項目檔案中,也隻有Irrlicht.h檔案和CIrrDeviceWin32.cpp中用到了這個修飾符,而且前一個檔案中定義此修飾符也是在聲明createDevice函數時要使用。說明createDevice函數才是引擎的真正入口,也是外部調用的唯一入口。

再來看看函數CIrrDeviceWin32做了什麼,CIrrDeviceWin32函數也是定義在CIrrDeviceWin32.cpp檔案中:

CIrrDeviceWin32::CIrrDeviceWin32(video::EDriverType driverType,

const core::dimension2d<s32>& windowSize,

u32 bits, bool fullscreen, IEventReceiver* receiver)

: VideoDriver(0), SceneManager(0), HWnd(0), ChangedToFullScreen(false)

{

// create filesystem,建立檔案系統

FileSystem = io::createFileSystem();

// create window,建立視窗

//擷取目前應用程式句柄

HINSTANCE hInstance = GetModuleHandle(0);

#ifdef _DEBUG

setDebugName("CIrrDeviceWin32");

#endif

//裝置類型不為NULL

if (driverType != video::DT_NULL)

{

// create the window, only if we do not use the null device

const c8* ClassName = "CIrrDeviceWin32";

// Register Class,注冊視窗類

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = (WNDPROC)WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = NULL;

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wcex.lpszMenuName = 0;

wcex.lpszClassName = ClassName;

wcex.hIconSm = 0;

RegisterClassEx(&wcex);

// calculate client size,設定視窗客戶區大小

RECT clientSize;

clientSize.top = 0;

clientSize.left = 0;

clientSize.right = windowSize.Width;

clientSize.bottom = windowSize.Height;

//如果為全屏模式

DWORD style = WS_POPUP;

//如果不為全屏模式,讓視窗處于螢幕正中間

if (!fullscreen)

style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

//計算客戶區矩形大小

AdjustWindowRect(&clientSize, style, FALSE);

const s32 WIDTH = clientSize.right - clientSize.left;

const s32 HEIGHT = clientSize.bottom - clientSize.top;

s32 WINDOW_LEFT = (GetSystemMetrics(SM_CXSCREEN) - WIDTH) / 2;

s32 WINDOW_TOP = (GetSystemMetrics(SM_CYSCREEN) - HEIGHT) / 2;

//如果為全屏

if (fullscreen)

{

WINDOW_LEFT = 0;

WINDOW_TOP = 0;

}

// create window,建立視窗

HWnd = CreateWindow( ClassName, "", style, WINDOW_LEFT, WINDOW_TOP,

WIDTH, HEIGHT, NULL, NULL, hInstance, NULL);

ShowWindow(HWnd , SW_SHOW);

UpdateWindow(HWnd);

}

// create driver,建立圖形驅動

createDriver(driverType, windowSize, bits, fullscreen);

// create gui environment,建立GUI環境

GUIEnvironment = gui::createGUIEnvironment(FileSystem, VideoDriver, receiver);

// create Scene manager,建立場景管理器

SceneManager = scene::createSceneManager(VideoDriver, FileSystem);

// register environment,使視窗與GUI環境關聯起來

SEnvMapper em;

em.Env = GUIEnvironment;

em.hWnd = HWnd;

//EnvMap為一個全局的list,裡面存儲着GUI與視窗句柄的映射關系

EnvMap.push_back(em);

}

在CIrrDeviceWin32的構造函數裡面,先後完成了注冊視窗類,建立視窗等工作後,最底下有一句代碼将句柄和window類的指針加入到全局的EnvMap裡面,友善在視窗消息來臨的時候進行回調。一般來說,遊戲的視窗都很少,是以這樣對速度也不會有太大影響。

Irrlicht0.1版本并沒有提供跨平台的支援,下面是新版本中有關建立裝置時的跨平台實作部分,由于我目前隻研究0.1版本,是以暫不作詳細讨論。

extern "C" IRRLICHT_API IrrlichtDevice* IRRCALLCONV createDeviceEx(const SIrrlichtCreationParameters& params)

{

IrrlichtDevice* dev = 0;

#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_

if (params.DeviceType == EIDT_WIN32 || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceWin32(params);

#endif

#ifdef _IRR_COMPILE_WITH_OSX_DEVICE_

if (params.DeviceType == EIDT_OSX || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceMacOSX(params);

#endif

#ifdef _IRR_COMPILE_WITH_WINDOWS_CE_DEVICE_

if (params.DeviceType == EIDT_WINCE || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceWinCE(params);

#endif

#ifdef _IRR_COMPILE_WITH_X11_DEVICE_

if (params.DeviceType == EIDT_X11 || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceLinux(params);

#endif

#ifdef _IRR_COMPILE_WITH_SDL_DEVICE_

if (params.DeviceType == EIDT_SDL || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceSDL(params);

#endif

#ifdef _IRR_COMPILE_WITH_FB_DEVICE_

if (params.DeviceType == EIDT_FRAMEBUFFER || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceFB(params);

#endif

#ifdef _IRR_COMPILE_WITH_CONSOLE_DEVICE_

if (params.DeviceType == EIDT_CONSOLE || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceConsole(params);

#endif

if (dev && !dev->getVideoDriver() && params.DriverType != video::EDT_NULL)

{

dev->closeDevice(); // destroy window

dev->run(); // consume quit message

dev->drop();

dev = 0;

}

return dev;

}

IRRLICHT_API IrrlichtDevice* IRRCALLCONV createDevice(video::E_DRIVER_TYPE driverType,

const core::dimension2d<u32>& windowSize,

u32 bits, bool fullscreen,

bool stencilbuffer, bool vsync, IEventReceiver* res)

{

SIrrlichtCreationParameters p;

p.DriverType = driverType;

p.WindowSize = windowSize;

p.Bits = (u8)bits;

p.Fullscreen = fullscreen;

p.Stencilbuffer = stencilbuffer;

p.Vsync = vsync;

p.EventReceiver = res;

return createDeviceEx(p);

}

extern "C" IRRLICHT_API IrrlichtDevice* IRRCALLCONV createDeviceEx(const SIrrlichtCreationParameters& params)

{

IrrlichtDevice* dev = 0;

#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_

if (params.DeviceType == EIDT_WIN32 || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceWin32(params);

#endif

#ifdef _IRR_COMPILE_WITH_OSX_DEVICE_

if (params.DeviceType == EIDT_OSX || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceMacOSX(params);

#endif

#ifdef _IRR_COMPILE_WITH_WINDOWS_CE_DEVICE_

if (params.DeviceType == EIDT_WINCE || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceWinCE(params);

#endif

#ifdef _IRR_COMPILE_WITH_X11_DEVICE_

if (params.DeviceType == EIDT_X11 || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceLinux(params);

#endif

#ifdef _IRR_COMPILE_WITH_SDL_DEVICE_

if (params.DeviceType == EIDT_SDL || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceSDL(params);

#endif

#ifdef _IRR_COMPILE_WITH_FB_DEVICE_

if (params.DeviceType == EIDT_FRAMEBUFFER || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceFB(params);

#endif

#ifdef _IRR_COMPILE_WITH_CONSOLE_DEVICE_

if (params.DeviceType == EIDT_CONSOLE || (!dev && params.DeviceType == EIDT_BEST))

dev = new CIrrDeviceConsole(params);

#endif

if (dev && !dev->getVideoDriver() && params.DriverType != video::EDT_NULL)

{

dev->closeDevice(); // destroy window

dev->run(); // consume quit message

dev->drop();

dev = 0;

}

return dev;

}

在新版本中,createDeviceEx利用宏隔離各個平台的差異,這裡産生了一個CIrrDeviceWin32對象。

加載模型

構造函數我們看完了,現在回到main函數,下面幾句設定視窗标題(其實裡面就調用了SetWindowText),然後添加一個text為"Hello World!This is the first Irrlicht Demo of zhangjun!"的靜态控件(我其實是用的我寫的一個例子來說了,^_^),Irr裡面用IGUIEnvironment接口來對視窗裡面的控件進行管理,這個接口定義了一系列的增加控件的方法。

//設定視窗标題

pDevice->setWindowCaption(L"Hello World!");

//擷取視訊裝置,場景管理器和使用者圖形環境的指針并存儲起來。

IVideoDriver *pVDriver = pDevice->getVideoDriver();

ISceneManager *pSManager = pDevice->getSceneManager();

IGUIEnvironment *pGuienv = pDevice->getGUIEnvironment();

//使用圖形使用者界面繪制一個Lable

pGuienv->addStaticText(_T("Hello World!This is the first Irrlicht Demo of zhangjun!"),false,core::rectEx<s32>(10,10,200,50),0);

接着是讀取一個3D模型,并設定其紋理,光照屬性,然後添加一個錄影機。此部分在以後再讨論,在些不詳述。

//讀取一個模型并顯示它

IAnimatedMesh *pAnMesh = pSManager->getMesh("..//..//media//sydney.md2");

IAnimatedMeshSceneNode *pAniNode =

pSManager->addAnimatedMeshSceneNode(pAnMesh);

//對模型進行設定

if (pAniNode)

{

//設定材質标記

pAniNode->setMaterialFlag(EMF_LIGHTING,false);

pAniNode->setFrameLoop(0,1650);//0.1版與新版有差別,為什麼MD2格式要近1600幀,資料說隻有199幀的???

pAniNode->setAnimationSpeed(240);//設定動畫播放速度

pAniNode->setMaterialTexture(0,pVDriver->getTexture("..//..//media//sydney.bmp"));

}

//添加攝像頭,設定其位置

pSManager->addCameraSceneNode(0,vector3df(0,30,-40),vector3df(0,5,0));

主循環

再往下面,就是遊戲程式設計的典型邏輯,在run裡面對Windows消息進行處理,不處理消息的時候就進行繪圖。收到close消息的時候循環退出。

//循環

while (pDevice->run())

{

pVDriver->beginScene(true,true,Color(0,100,100,100));//0.1版與新版有差別,新版有預設參數

pSManager->drawAll();

pGuienv->drawAll();

pVDriver->endScene();//繪圖完成後,完全前後緩存的交換。

}

//釋放裝置

pDevice->drop();

return 0;

在beginScene和endScene之間完成繪圖的邏輯。我們現在隻看看消息處理的部分,在device->run裡面可以看到,跟典型的Windows消息派發一樣,現在我們來看看消息處理函數:

//! runs the device. Returns false if device wants to be deleted

bool CIrrDeviceWin32::run()

{

MSG msg;

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return (msg.message != WM_QUIT);

}

在全局函數WndProc(位于CIrrDeviceWin32.cpp)裡面,将消息轉換為Irr的Event格式,然後從全局表裡面找到視窗的this指針,最後調用postEventFromUser進行轉發消息。如果有時間的話,我再看一下irr的GUI部分及事件處理機制。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

irr::gui::IGUIEnvironment* Env = 0;

irr::SEvent event;

switch (message)

{

case WM_PAINT:

{

PAINTSTRUCT ps;

HDC hdc = BeginPaint(hWnd, &ps);

EndPaint(hWnd, &ps);

}

return 0;

case WM_ERASEBKGND:

return 0;

case WM_LBUTTONDOWN:

event.EventType = irr::EET_MOUSE_INPUT_EVENT;

event.MouseInput.Event = irr::EMIE_LMOUSE_PRESSED_DOWN;

event.MouseInput.X = LOWORD(lParam);

event.MouseInput.Y = HIWORD(lParam);

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_LBUTTONUP:

event.EventType = irr::EET_MOUSE_INPUT_EVENT;

event.MouseInput.Event = irr::EMIE_LMOUSE_LEFT_UP;

event.MouseInput.X = LOWORD(lParam);

event.MouseInput.Y = HIWORD(lParam);

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_RBUTTONDOWN:

event.EventType = irr::EET_MOUSE_INPUT_EVENT;

event.MouseInput.Event = irr::EMIE_RMOUSE_PRESSED_DOWN;

event.MouseInput.X = LOWORD(lParam);

event.MouseInput.Y = HIWORD(lParam);

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_RBUTTONUP:

event.EventType = irr::EET_MOUSE_INPUT_EVENT;

event.MouseInput.Event = irr::EMIE_RMOUSE_LEFT_UP;

event.MouseInput.X = LOWORD(lParam);

event.MouseInput.Y = HIWORD(lParam);

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_MBUTTONDOWN:

event.EventType = irr::EET_MOUSE_INPUT_EVENT;

event.MouseInput.Event = irr::EMIE_MMOUSE_PRESSED_DOWN;

event.MouseInput.X = LOWORD(lParam);

event.MouseInput.Y = HIWORD(lParam);

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_MBUTTONUP:

event.EventType = irr::EET_MOUSE_INPUT_EVENT;

event.MouseInput.Event = irr::EMIE_MMOUSE_LEFT_UP;

event.MouseInput.X = LOWORD(lParam);

event.MouseInput.Y = HIWORD(lParam);

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_MOUSEMOVE:

event.EventType = irr::EET_MOUSE_INPUT_EVENT;

event.MouseInput.Event = irr::EMIE_MOUSE_MOVED;

event.MouseInput.X = LOWORD(lParam);

event.MouseInput.Y = HIWORD(lParam);

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_KEYDOWN:

event.EventType = irr::EET_KEY_INPUT_EVENT;

event.KeyInput.Key = (irr::s32)wParam;

event.KeyInput.PressedDown = true;

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_KEYUP:

event.EventType = irr::EET_KEY_INPUT_EVENT;

event.KeyInput.Key = (irr::s32)wParam;

event.KeyInput.PressedDown = false;

Env = getEnvironmentFromHWnd(hWnd);

if (Env)

Env->postEventFromUser(event);

return 0;

case WM_DESTROY:

PostQuitMessage(0);

return 0;

case WM_SYSCOMMAND:

// prevent screensaver or monitor powersave mode from starting

if (wParam == SC_SCREENSAVE ||

wParam == SC_MONITORPOWER)

return 0;

break;

}

return DefWindowProc(hWnd, message, wParam, lParam);

}

整理參考:

hellphenix的專欄:http://blog.csdn.net/hellphenix/archive/2008/03/19/2198226.aspx

小時候可靓了:http://blog.csdn.net/wqjqepr/archive/2010/04/26/5528528.aspx

遊戲的行者:http://blog.csdn.net/n5/archive/2009/07/08/4329603.aspx

遊戲的行者:http://blog.csdn.net/n5/archive/2008/12/15/3516219.aspx

遊戲的行者:http://blog.csdn.net/n5/archive/2009/07/05/4323332.aspx

遊戲的行者:http://blog.csdn.net/n5/archive/2009/07/12/4342758.aspx

Flyflyking:http://blog.csdn.net/flyflyking/archive/2011/03/30/6289698.aspx