天天看点

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