正如其它的教程开始一样:包含必要的头文件,链接上IrrLicht引擎用到的*.lib库文件,并声明了一些全局变量。我们还添加了两个'using namespece'声明,因此不用在类的前面写所属的命名空间。在这个教程中,我们要用大量gui命名空间中的类。
#include <irrlicht.h> #include <iostream>
using namespace irr; using namespace gui;
#pragma comment(lib, "Irrlicht.lib")
IrrlichtDevice *Device = 0; core::stringc StartUpModelFile; core::stringw MessageText; core::stringw Caption; scene::IAnimatedMeshSceneNode* Model = 0; scene::ISceneNode* SkyBox = 0;
|
下面三个是网格查看器要实现的函数。第一个函数showAboutText()简单显示一个有标题和消息文本的消息框。这些文本被保存在开始的MessageText和Caption字符串变量里。
void showAboutText() { // create modal message box with the text // loaded from the xml file. Device->getGUIEnvironment()->addMessageBox( Caption.c_str(), MessageText.c_str()); }
|
第二个函数loadModel()用场景管理器的addAnimatedMeshSceneNode 方法加载并显示一个模型。毫无困难吧。如果指定的模型不能加载,则会显示一个消息框。
void loadModel(const c8* filename) { // load a model into the engine if (Model) Model->remove(); Model = 0;
scene::IAnimatedMesh* m = Device->getSceneManager()->getMesh(filename); if (!m) { // model could not be loaded if (StartUpModelFile != filename) Device->getGUIEnvironment()->addMessageBox( Caption.c_str(), L"The model could not be loaded. " \ L"Maybe it is not a supported file format."); return; }
// set default material properties Model = Device->getSceneManager()->addAnimatedMeshSceneNode(m); Model->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); Model->setMaterialFlag(video::EMF_LIGHTING, false); Model->setDebugDataVisible(true); }
|
最后的这个函数createToolBox()是创建一个工具箱窗口。在这个简单的网格查看器里,这个工具箱只包含一个选项卡和三个用于改变显示模型比例的编辑框。
void createToolBox() { // remove tool box if already there IGUIEnvironment* env = Device->getGUIEnvironment(); IGUIElement* root = env->getRootGUIElement(); IGUIElement* e = root->getElementFromId(5000, true); if (e) e->remove();
// create the toolbox window IGUIWindow* wnd = env->addWindow(core::rect<s32>(450,25,640,480), false, L"Toolset", 0, 5000);
// create tab control and tabs IGUITabControl* tab = env->addTabControl( core::rect<s32>(2,20,640-452,480-7), wnd, true, true); IGUITab* t1 = tab->addTab(L"Scale"); IGUITab* t2 = tab->addTab(L"Empty Tab");
// add some edit boxes and a button to tab one env->addEditBox(L"1.0", core::rect<s32>(40,50,130,70), true, t1, 901); env->addEditBox(L"1.0", core::rect<s32>(40,80,130,100), true, t1, 902); env->addEditBox(L"1.0", core::rect<s32>(40,110,130,130), true, t1, 903); env->addButton(core::rect<s32>(10,150,100,190), t1, 1101, L"set");
// bring irrlicht engine logo to front, because it // now may be below the newly created toolbox root->bringToFront(root->getElementFromId(666, true)); }
|
要处理全部GUI元产生的事件,我们需要创建一个事件接收器。这个实在太简单了。如果有事件产生,则检查事件的类型和产生事件的GUI元id,并执行符合这个条件的代码。例如,一个GUI元id为100的菜单事件产生,则打开一个文件打开对话框(file-open-dialog)。
class MyEventReceiver : public IEventReceiver { public: virtual bool OnEvent(SEvent event) { if (event.EventType == EET_GUI_EVENT) { s32 id = event.GUIEvent.Caller->getID(); IGUIEnvironment* env = Device->getGUIEnvironment(); switch(event.GUIEvent.EventType) { case EGET_MENU_ITEM_SELECTED: { // a menu item was clicked IGUIContextMenu* menu = (IGUIContextMenu*)event.GUIEvent.Caller; s32 id = menu->getItemCommandId(menu->getSelectedItem()); switch(id) { case 100: // File -> Open Model env->addFileOpenDialog(L"Please select a model file to open"); break; case 200: // File -> Quit Device->closeDevice(); break; case 300: // View -> Skybox SkyBox->setVisible(!SkyBox->isVisible()); break; case 400: // View -> Debug Information if (Model) Model->setDebugDataVisible(!Model->isDebugDataVisible()); break; case 500: // Help->About showAboutText(); break; case 610: // View -> Material -> Solid if (Model) Model->setMaterialType(video::EMT_SOLID); break; case 620: // View -> Material -> Transparent if (Model) Model->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); break; case 630: // View -> Material -> Reflection if (Model) Model->setMaterialType(video::EMT_SPHERE_MAP); break; } break; } case EGET_FILE_SELECTED: { // load the model file, selected in the file open dialog IGUIFileOpenDialog* dialog = (IGUIFileOpenDialog*)event.GUIEvent.Caller; loadModel(core::stringc(dialog->getFilename()).c_str()); } case EGET_BUTTON_CLICKED: switch(id) { case 1101: { // set scale gui::IGUIElement* root = env->getRootGUIElement(); core::vector3df scale; core::stringc s; s = root->getElementFromId(901, true)->getText(); scale.X = (f32)atof(s.c_str()); s = root->getElementFromId(902, true)->getText(); scale.Y = (f32)atof(s.c_str()); s = root->getElementFromId(903, true)->getText(); scale.Z = (f32)atof(s.c_str()); if (Model) Model->setScale(scale); } break; case 1102: env->addFileOpenDialog(L"Please select a model file to open"); break; case 1103: showAboutText(); break; case 1104: createToolBox(); break; } break; } } return false; } };
|
本教程最难的地方已经实现了。现在只需创建IrrLicht引擎的设备,按钮,菜单和工具栏。好像之前那样用createDevice()开始引擎的使用。为了使程序处理事件,我们设置刚才定义的事件接收器作为参数。#ifdef WIN32这句预编译指令不是必需的,但我包含这语句就可以使教程在windows系统中使用DirectX,而在其它系统(如Linux系统)中使用OpenGL。正如你所见的,调用了一个之前没出现过的IrrlichtDevice::setResizeAble()函数。这样能使渲染窗口可调整大小,对于网格查看器来说是十分有用的。
int main() { // ask user for driver
video::E_DRIVER_TYPE driverType;
printf("Please select the driver you want for this example:\n"\ " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\ " (d) Software Renderer\n (e) Apfelbaum Software Renderer\n"\ " (f) NullDevice\n (otherKey) exit\n\n");
char key; std::cin >> key;
switch(key) { case 'a': driverType = video::EDT_DIRECT3D9;break; case 'b': driverType = video::EDT_DIRECT3D8;break; case 'c': driverType = video::EDT_OPENGL; break; case 'd': driverType = video::EDT_SOFTWARE; break; case 'e': driverType = video::EDT_SOFTWARE2;break; case 'f': driverType = video::EDT_NULL; break; default: return 1; }
// create device and exit if creation failed
MyEventReceiver receiver; Device = createDevice(driverType, core::dimension2d<s32>(640, 480), 16, false, false, false, &receiver);
if (Device == 0) return 1; // could not create selected driver.
Device->setResizeAble(true); Device->setWindowCaption(L"Irrlicht Engine - Loading...");
video::IVideoDriver* driver = Device->getVideoDriver(); IGUIEnvironment* env = Device->getGUIEnvironment(); scene::ISceneManager* smgr = Device->getSceneManager();
|
下一个步是读取配置文件。配置文件被保存成类似下面的*.xml格式文件:
<?xml version="1.0"?> <config> <startUpModel file="some filename" /> <messageText caption="Irrlicht Engine Mesh Viewer"> Hello! </messageText> </config>
我们把需要的数据保存到StartUpModelFile, MessageText和Caption这三个全局变量里。下面使用了IrrLicht引擎的XML分析器:
// read configuration from xml file io::IXMLReader* xml = Device->getFileSystem()->createXMLReader("../../media/config.xml"); while(xml && xml->read()) { switch(xml->getNodeType()) { case io::EXN_TEXT: // in this xml file, the only text which occurs is the messageText MessageText = xml->getNodeData(); break; case io::EXN_ELEMENT: { if (core::stringw("startUpModel") == xml->getNodeName()) StartUpModelFile = xml->getAttributeValue(L"file"); else if (core::stringw("messageText") == xml->getNodeName()) Caption = xml->getAttributeValue(L"caption"); } break; } } if (xml) xml->drop(); // don't forget to delete the xml reader
|
这都并不难。现在要设置漂亮的字体并创建菜单。还能为菜单项创建子菜单。例如调用menu->addItem(L"File", -1, true, true)就添加一个新菜单项(名字为“File”,id为-1)。接着的参数是设置菜单是否可用,是不是最末项(是否还有子菜单)。这个子菜单可以用menu->getSubMenu(0)来访问了,因为“File”菜单项的索引为0。
// set a nicer font IGUISkin* skin = env->getSkin(); IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp"); if (font) skin->setFont(font);
// create menu gui::IGUIContextMenu* menu = env->addMenu(); menu->addItem(L"File", -1, true, true); menu->addItem(L"View", -1, true, true); menu->addItem(L"Help", -1, true, true);
gui::IGUIContextMenu* submenu; submenu = menu->getSubMenu(0); submenu->addItem(L"Open Model File...", 100); submenu->addSeparator(); submenu->addItem(L"Quit", 200);
submenu = menu->getSubMenu(1); submenu->addItem(L"toggle sky box visibility", 300); submenu->addItem(L"toggle model debug information", 400); submenu->addItem(L"model material", -1, true, true );
submenu = submenu->getSubMenu(2); submenu->addItem(L"Solid", 610); submenu->addItem(L"Transparent", 620); submenu->addItem(L"Reflection", 630);
submenu = menu->getSubMenu(2); submenu->addItem(L"About", 500);
|
我们还添加了一个工具栏,并在上面放置了几个图像按钮和一个选项功能无效的组合框(译注:新版本中实现了)。
// create toolbar gui::IGUIToolBar* bar = env->addToolBar(); bar->addButton(1102, 0, driver->getTexture("../../media/open.bmp")); bar->addButton(1103, 0, driver->getTexture("../../media/help.bmp")); bar->addButton(1104, 0, driver->getTexture("../../media/tools.bmp"));
// create a combobox with some senseless texts gui::IGUIComboBox* box = env->addComboBox(core::rect<s32>(100,5,200,25), bar); box->addItem(L"Bilinear"); box->addItem(L"Trilinear"); box->addItem(L"Anisotropic"); box->addItem(L"Isotropic"); box->addItem(L"Psychedelic"); box->addItem(L"No filtering");
|
为了使编辑器更好看,我们取消了GUI元的透明,并加上IrrLicht引擎的标志。另外,创建一个文本来显示当前的帧(frame per second ),还有改变窗口标题。
// disable alpha for (s32 i=0; i<gui::EGDC_COUNT ; ++i) { video::SColor col = env->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); col.setAlpha(255); env->getSkin()->setColor((gui::EGUI_DEFAULT_COLOR)i, col); }
// add a tabcontrol createToolBox();
// add the irrlicht engine logo IGUIImage* img = env->addImage(core::rect<s32>(22,429,108,460), 0, 666); img->setImage(driver->getTexture("../../media/irrlichtlogoaligned.jpg"));
// create fps text IGUIStaticText* fpstext = env->addStaticText(L"", core::rect<s32>(210,26,270,41), true);
// set window caption Caption += " - ["; Caption += driver->getName(); Caption += "]"; Device->setWindowCaption(Caption.c_str());
|
程序已经基本完整了。还要在程序的开始显示一个about对话框,和加载并显示一个模型。为了易于观察模型,还创建一个天空盒和一个受用户控制的摄像机(maya类型摄像机)。最后,用标准的绘制循环绘制全部物体。
// show about message box and load default model showAboutText(); loadModel(StartUpModelFile.c_str());
// add skybox SkyBox = smgr->addSkyBoxSceneNode( driver->getTexture("../../media/irrlicht2_up.bmp"), driver->getTexture("../../media/irrlicht2_dn.bmp"), driver->getTexture("../../media/irrlicht2_lf.bmp"), driver->getTexture("../../media/irrlicht2_rt.bmp"), driver->getTexture("../../media/irrlicht2_ft.bmp"), driver->getTexture("../../media/irrlicht2_bk.bmp"));
// add a camera scene node smgr->addCameraSceneNodeMaya(); // draw everything while(Device->run() && driver) if (Device->isWindowActive()) { driver->beginScene(true, true, video::SColor(150,50,50,50)); smgr->drawAll(); env->drawAll(); driver->endScene();
core::stringw str = L"FPS: "; str += driver->getFPS(); fpstext->setText(str.c_str()); } Device->drop(); return 0; }
|
编译和运行这些代码,你就会有一个完整功能的3d网格查看器了。
|