设备的创建
同时也可以看到,使用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