irrwowview

Tutorial 9: Mesh Viewer

Tutorial 9: Mesh Viewer
本教程要演示如何创建一个更复杂的应用程序。我们使用IrrLicht的用户界面API和场景管理构建一个简单的网格查看器(mesh viewer)。
本教程演示如何创建和使用按钮,窗口,工具栏,菜单,组合框,选项卡,编辑框,图像框,消息框和天空盒,并演示如何用IrrLicht引擎的XML读入器分析XML文件。

程序的效果如下图所示:

 

Lets start!

正如其它的教程开始一样:包含必要的头文件,链接上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()简单显示一个有标题和消息文本的消息框。这些文本被保存在开始的MessageTextCaption字符串变量里。

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元产生的事件,我们需要创建一个事件接收器。这个实在太简单了。如果有事件产生,则检查事件的类型和产生事件的GUIid,并执行符合这个条件的代码。例如,一个GUIid100的菜单事件产生,则打开一个文件打开对话框(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 MessageTextCaption这三个全局变量里。下面使用了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网格查看器了。

 

 

posted on 2008-07-04 11:09 shjy 阅读(713) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理