裝置的建立
同時也可以看到,使用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