1 /** Example 009 Mesh Viewer
2 
3 This tutorial show how to create a more complex application with the engine.
4 We construct a simple mesh viewer using the user interface API and the
5 scene management of Irrlicht. The tutorial show how to create and use Buttons,
6 Windows, Toolbars, Menus, ComboBoxes, Tabcontrols, Editboxes, Images,
7 MessageBoxes, SkyBoxes, and how to parse XML files with the integrated XML
8 reader of the engine.
9 
10 We start like in most other tutorials: Include all necessary header files, add
11 a comment to let the engine be linked with the right .lib file in Visual
12 Studio, and declare some global variables. We also add two 'using namespace'
13 statements, so we do not need to write the whole names of all classes. In this
14 tutorial, we use a lot stuff from the gui namespace.
15 */
16 #include <irrlicht.h>
17 #include "driverChoice.h"
18 
19 using namespace irr;
20 using namespace gui;
21 
22 #ifdef _MSC_VER
23 #pragma comment(lib, "Irrlicht.lib")
24 #endif
25 
26 
27 /*
28 Some global variables used later on
29 */
30 IrrlichtDevice *Device = 0;
31 core::stringc StartUpModelFile;
32 core::stringw MessageText;
33 core::stringw Caption;
34 scene::ISceneNode* Model = 0;
35 scene::ISceneNode* SkyBox = 0;
36 bool Octree=false;
37 bool UseLight=false;
38 
39 scene::ICameraSceneNode* Camera[2] = {0, 0};
40 
41 // Values used to identify individual GUI elements
42 enum
43 {
44 	GUI_ID_DIALOG_ROOT_WINDOW  = 0x10000,
45 
46 	GUI_ID_X_SCALE,
47 	GUI_ID_Y_SCALE,
48 	GUI_ID_Z_SCALE,
49 
50 	GUI_ID_OPEN_MODEL,
51 	GUI_ID_SET_MODEL_ARCHIVE,
52 	GUI_ID_LOAD_AS_OCTREE,
53 
54 	GUI_ID_SKY_BOX_VISIBLE,
55 	GUI_ID_TOGGLE_DEBUG_INFO,
56 
57 	GUI_ID_DEBUG_OFF,
58 	GUI_ID_DEBUG_BOUNDING_BOX,
59 	GUI_ID_DEBUG_NORMALS,
60 	GUI_ID_DEBUG_SKELETON,
61 	GUI_ID_DEBUG_WIRE_OVERLAY,
62 	GUI_ID_DEBUG_HALF_TRANSPARENT,
63 	GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES,
64 	GUI_ID_DEBUG_ALL,
65 
66 	GUI_ID_MODEL_MATERIAL_SOLID,
67 	GUI_ID_MODEL_MATERIAL_TRANSPARENT,
68 	GUI_ID_MODEL_MATERIAL_REFLECTION,
69 
70 	GUI_ID_CAMERA_MAYA,
71 	GUI_ID_CAMERA_FIRST_PERSON,
72 
73 	GUI_ID_POSITION_TEXT,
74 
75 	GUI_ID_ABOUT,
76 	GUI_ID_QUIT,
77 
78 	GUI_ID_TEXTUREFILTER,
79 	GUI_ID_SKIN_TRANSPARENCY,
80 	GUI_ID_SKIN_ANIMATION_FPS,
81 
82 	GUI_ID_BUTTON_SET_SCALE,
83 	GUI_ID_BUTTON_SCALE_MUL10,
84 	GUI_ID_BUTTON_SCALE_DIV10,
85 	GUI_ID_BUTTON_OPEN_MODEL,
86 	GUI_ID_BUTTON_SHOW_ABOUT,
87 	GUI_ID_BUTTON_SHOW_TOOLBOX,
88 	GUI_ID_BUTTON_SELECT_ARCHIVE,
89 
90 	GUI_ID_ANIMATION_INFO,
91 
92 	// And some magic numbers
93 	MAX_FRAMERATE = 80,
94 	DEFAULT_FRAMERATE = 30
95 };
96 
97 
98 /*
99 Toggle between various cameras
100 */
setActiveCamera(scene::ICameraSceneNode * newActive)101 void setActiveCamera(scene::ICameraSceneNode* newActive)
102 {
103 	if (0 == Device)
104 		return;
105 
106 	scene::ICameraSceneNode * active = Device->getSceneManager()->getActiveCamera();
107 	active->setInputReceiverEnabled(false);
108 
109 	newActive->setInputReceiverEnabled(true);
110 	Device->getSceneManager()->setActiveCamera(newActive);
111 }
112 
113 /*
114 	Set the skin transparency by changing the alpha values of all skin-colors
115 */
setSkinTransparency(s32 alpha,irr::gui::IGUISkin * skin)116 void setSkinTransparency(s32 alpha, irr::gui::IGUISkin * skin)
117 {
118 	for (s32 i=0; i<irr::gui::EGDC_COUNT ; ++i)
119 	{
120 		video::SColor col = skin->getColor((EGUI_DEFAULT_COLOR)i);
121 		col.setAlpha(alpha);
122 		skin->setColor((EGUI_DEFAULT_COLOR)i, col);
123 	}
124 }
125 
126 /*
127   Update the display of the model scaling
128 */
updateScaleInfo(scene::ISceneNode * model)129 void updateScaleInfo(scene::ISceneNode* model)
130 {
131 	IGUIElement* toolboxWnd = Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
132 	if (!toolboxWnd)
133 		return;
134 	if (!model)
135 	{
136 		toolboxWnd->getElementFromId(GUI_ID_X_SCALE, true)->setText( L"-" );
137 		toolboxWnd->getElementFromId(GUI_ID_Y_SCALE, true)->setText( L"-" );
138 		toolboxWnd->getElementFromId(GUI_ID_Z_SCALE, true)->setText( L"-" );
139 	}
140 	else
141 	{
142 		core::vector3df scale = model->getScale();
143 		toolboxWnd->getElementFromId(GUI_ID_X_SCALE, true)->setText( core::stringw(scale.X).c_str() );
144 		toolboxWnd->getElementFromId(GUI_ID_Y_SCALE, true)->setText( core::stringw(scale.Y).c_str() );
145 		toolboxWnd->getElementFromId(GUI_ID_Z_SCALE, true)->setText( core::stringw(scale.Z).c_str() );
146 	}
147 }
148 
149 /*
150 Function showAboutText() displays a messagebox with a caption and
151 a message text. The texts will be stored in the MessageText and Caption
152 variables at startup.
153 */
showAboutText()154 void showAboutText()
155 {
156 	// create modal message box with the text
157 	// loaded from the xml file.
158 	Device->getGUIEnvironment()->addMessageBox(
159 		Caption.c_str(), MessageText.c_str());
160 }
161 
162 
163 /*
164 Function loadModel() loads a model and displays it using an
165 addAnimatedMeshSceneNode and the scene manager. Nothing difficult. It also
166 displays a short message box, if the model could not be loaded.
167 */
loadModel(const c8 * fn)168 void loadModel(const c8* fn)
169 {
170 	// modify the name if it a .pk3 file
171 
172 	io::path filename(fn);
173 
174 	io::path extension;
175 	core::getFileNameExtension(extension, filename);
176 	extension.make_lower();
177 
178 	// if a texture is loaded apply it to the current model..
179 	if (extension == ".jpg" || extension == ".pcx" ||
180 		extension == ".png" || extension == ".ppm" ||
181 		extension == ".pgm" || extension == ".pbm" ||
182 		extension == ".psd" || extension == ".tga" ||
183 		extension == ".bmp" || extension == ".wal" ||
184 		extension == ".rgb" || extension == ".rgba")
185 	{
186 		video::ITexture * texture =
187 			Device->getVideoDriver()->getTexture( filename );
188 		if ( texture && Model )
189 		{
190 			// always reload texture
191 			Device->getVideoDriver()->removeTexture(texture);
192 			texture = Device->getVideoDriver()->getTexture( filename );
193 
194 			Model->setMaterialTexture(0, texture);
195 		}
196 		return;
197 	}
198 	// if a archive is loaded add it to the FileArchive..
199 	else if (extension == ".pk3" || extension == ".zip" || extension == ".pak" || extension == ".npk")
200 	{
201 		Device->getFileSystem()->addFileArchive(filename.c_str());
202 		return;
203 	}
204 
205 	// load a model into the engine
206 
207 	if (Model)
208 		Model->remove();
209 
210 	Model = 0;
211 
212 	if (extension==".irr")
213 	{
214 		core::array<scene::ISceneNode*> outNodes;
215 		Device->getSceneManager()->loadScene(filename);
216 		Device->getSceneManager()->getSceneNodesFromType(scene::ESNT_ANIMATED_MESH, outNodes);
217 		if (outNodes.size())
218 			Model = outNodes[0];
219 		return;
220 	}
221 
222 	scene::IAnimatedMesh* m = Device->getSceneManager()->getMesh( filename.c_str() );
223 
224 	if (!m)
225 	{
226 		// model could not be loaded
227 
228 		if (StartUpModelFile != filename)
229 			Device->getGUIEnvironment()->addMessageBox(
230 			Caption.c_str(), L"The model could not be loaded. " \
231 			L"Maybe it is not a supported file format.");
232 		return;
233 	}
234 
235 	// set default material properties
236 
237 	if (Octree)
238 		Model = Device->getSceneManager()->addOctreeSceneNode(m->getMesh(0));
239 	else
240 	{
241 		scene::IAnimatedMeshSceneNode* animModel = Device->getSceneManager()->addAnimatedMeshSceneNode(m);
242 		animModel->setAnimationSpeed(30);
243 		Model = animModel;
244 	}
245 	Model->setMaterialFlag(video::EMF_LIGHTING, UseLight);
246 	Model->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, UseLight);
247 //	Model->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
248 	Model->setDebugDataVisible(scene::EDS_OFF);
249 
250 	// we need to uncheck the menu entries. would be cool to fake a menu event, but
251 	// that's not so simple. so we do it brute force
252 	gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId(GUI_ID_TOGGLE_DEBUG_INFO, true);
253 	if (menu)
254 		for(int item = 1; item < 6; ++item)
255 			menu->setItemChecked(item, false);
256 	updateScaleInfo(Model);
257 }
258 
259 
260 /*
261 Function createToolBox() creates a toolbox window. In this simple mesh
262 viewer, this toolbox only contains a tab control with three edit boxes for
263 changing the scale of the displayed model.
264 */
createToolBox()265 void createToolBox()
266 {
267 	// remove tool box if already there
268 	IGUIEnvironment* env = Device->getGUIEnvironment();
269 	IGUIElement* root = env->getRootGUIElement();
270 	IGUIElement* e = root->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
271 	if (e)
272 		e->remove();
273 
274 	// create the toolbox window
275 	IGUIWindow* wnd = env->addWindow(core::rect<s32>(600,45,800,480),
276 		false, L"Toolset", 0, GUI_ID_DIALOG_ROOT_WINDOW);
277 
278 	// create tab control and tabs
279 	IGUITabControl* tab = env->addTabControl(
280 		core::rect<s32>(2,20,800-602,480-7), wnd, true, true);
281 
282 	IGUITab* t1 = tab->addTab(L"Config");
283 
284 	// add some edit boxes and a button to tab one
285 	env->addStaticText(L"Scale:",
286 			core::rect<s32>(10,20,60,45), false, false, t1);
287 	env->addStaticText(L"X:", core::rect<s32>(22,48,40,66), false, false, t1);
288 	env->addEditBox(L"1.0", core::rect<s32>(40,46,130,66), true, t1, GUI_ID_X_SCALE);
289 	env->addStaticText(L"Y:", core::rect<s32>(22,82,40,96), false, false, t1);
290 	env->addEditBox(L"1.0", core::rect<s32>(40,76,130,96), true, t1, GUI_ID_Y_SCALE);
291 	env->addStaticText(L"Z:", core::rect<s32>(22,108,40,126), false, false, t1);
292 	env->addEditBox(L"1.0", core::rect<s32>(40,106,130,126), true, t1, GUI_ID_Z_SCALE);
293 
294 	env->addButton(core::rect<s32>(10,134,85,165), t1, GUI_ID_BUTTON_SET_SCALE, L"Set");
295 
296 	// quick scale buttons
297 	env->addButton(core::rect<s32>(65,20,95,40), t1, GUI_ID_BUTTON_SCALE_MUL10, L"* 10");
298 	env->addButton(core::rect<s32>(100,20,130,40), t1, GUI_ID_BUTTON_SCALE_DIV10, L"* 0.1");
299 
300 	updateScaleInfo(Model);
301 
302 	// add transparency control
303 	env->addStaticText(L"GUI Transparency Control:",
304 			core::rect<s32>(10,200,150,225), true, false, t1);
305 	IGUIScrollBar* scrollbar = env->addScrollBar(true,
306 			core::rect<s32>(10,225,150,240), t1, GUI_ID_SKIN_TRANSPARENCY);
307 	scrollbar->setMax(255);
308 	scrollbar->setPos(255);
309 
310 	// add framerate control
311 	env->addStaticText(L":", core::rect<s32>(10,240,150,265), true, false, t1);
312 	env->addStaticText(L"Framerate:",
313 			core::rect<s32>(12,240,75,265), false, false, t1);
314 	// current frame info
315 	env->addStaticText(L"", core::rect<s32>(75,240,200,265), false, false, t1,
316 			GUI_ID_ANIMATION_INFO);
317 	scrollbar = env->addScrollBar(true,
318 			core::rect<s32>(10,265,150,280), t1, GUI_ID_SKIN_ANIMATION_FPS);
319 	scrollbar->setMax(MAX_FRAMERATE);
320 	scrollbar->setMin(-MAX_FRAMERATE);
321 	scrollbar->setPos(DEFAULT_FRAMERATE);
322 	scrollbar->setSmallStep(1);
323 }
324 
325 /*
326 Function updateToolBox() is called each frame to update dynamic information in
327 the toolbox.
328 */
updateToolBox()329 void updateToolBox()
330 {
331 	IGUIEnvironment* env = Device->getGUIEnvironment();
332 	IGUIElement* root = env->getRootGUIElement();
333 	IGUIElement* dlg = root->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
334 	if (!dlg )
335 		return;
336 
337 	// update the info we have about the animation of the model
338 	IGUIStaticText *  aniInfo = (IGUIStaticText *)(dlg->getElementFromId(GUI_ID_ANIMATION_INFO, true));
339 	if (aniInfo)
340 	{
341 		if ( Model && scene::ESNT_ANIMATED_MESH == Model->getType() )
342 		{
343 			scene::IAnimatedMeshSceneNode* animatedModel = (scene::IAnimatedMeshSceneNode*)Model;
344 
345 			core::stringw str( (s32)core::round_(animatedModel->getAnimationSpeed()) );
346 			str += L" Frame: ";
347 			str += core::stringw((s32)animatedModel->getFrameNr());
348 			aniInfo->setText(str.c_str());
349 		}
350 		else
351 			aniInfo->setText(L"");
352 	}
353 }
354 
onKillFocus()355 void onKillFocus()
356 {
357 	// Avoid that the FPS-camera continues moving when the user presses alt-tab while
358 	// moving the camera.
359 	const core::list<scene::ISceneNodeAnimator*>& animators = Camera[1]->getAnimators();
360 	core::list<irr::scene::ISceneNodeAnimator*>::ConstIterator iter = animators.begin();
361 	while ( iter != animators.end() )
362 	{
363 		if ( (*iter)->getType() == scene::ESNAT_CAMERA_FPS )
364 		{
365 			// we send a key-down event for all keys used by this animator
366 			scene::ISceneNodeAnimatorCameraFPS * fpsAnimator = static_cast<scene::ISceneNodeAnimatorCameraFPS*>(*iter);
367 			const core::array<SKeyMap>& keyMap = fpsAnimator->getKeyMap();
368 			for ( irr::u32 i=0; i< keyMap.size(); ++i )
369 			{
370 				irr::SEvent event;
371 				event.EventType = EET_KEY_INPUT_EVENT;
372 				event.KeyInput.Key = keyMap[i].KeyCode;
373 				event.KeyInput.PressedDown = false;
374 				fpsAnimator->OnEvent(event);
375 			}
376 		}
377 		++iter;
378 	}
379 }
380 
381 /*
382 Function hasModalDialog() checks if we currently have a modal dialog open.
383 */
hasModalDialog()384 bool hasModalDialog()
385 {
386 	if ( !Device )
387 		return false;
388 	IGUIEnvironment* env = Device->getGUIEnvironment();
389 	IGUIElement * focused = env->getFocus();
390 	while ( focused )
391 	{
392 		if ( focused->isVisible() && focused->hasType(EGUIET_MODAL_SCREEN) )
393 			return true;
394 		focused = focused->getParent();
395 	}
396 	return false;
397 }
398 
399 /*
400 To get all the events sent by the GUI Elements, we need to create an event
401 receiver. This one is really simple. If an event occurs, it checks the id of
402 the caller and the event type, and starts an action based on these values. For
403 example, if a menu item with id GUI_ID_OPEN_MODEL was selected, it opens a file-open-dialog.
404 */
405 class MyEventReceiver : public IEventReceiver
406 {
407 public:
OnEvent(const SEvent & event)408 	virtual bool OnEvent(const SEvent& event)
409 	{
410 		// Escape swaps Camera Input
411 		if (event.EventType == EET_KEY_INPUT_EVENT &&
412 			event.KeyInput.PressedDown == false)
413 		{
414 			if ( OnKeyUp(event.KeyInput.Key) )
415 				return true;
416 		}
417 
418 		if (event.EventType == EET_GUI_EVENT)
419 		{
420 			s32 id = event.GUIEvent.Caller->getID();
421 			IGUIEnvironment* env = Device->getGUIEnvironment();
422 
423 			switch(event.GUIEvent.EventType)
424 			{
425 			case EGET_MENU_ITEM_SELECTED:
426 					// a menu item was clicked
427 					OnMenuItemSelected( (IGUIContextMenu*)event.GUIEvent.Caller );
428 				break;
429 
430 			case EGET_FILE_SELECTED:
431 				{
432 					// load the model file, selected in the file open dialog
433 					IGUIFileOpenDialog* dialog =
434 						(IGUIFileOpenDialog*)event.GUIEvent.Caller;
435 					loadModel(core::stringc(dialog->getFileName()).c_str());
436 				}
437 				break;
438 
439 			case EGET_SCROLL_BAR_CHANGED:
440 
441 				// control skin transparency
442 				if (id == GUI_ID_SKIN_TRANSPARENCY)
443 				{
444 					const s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
445 					setSkinTransparency(pos, env->getSkin());
446 				}
447 				// control animation speed
448 				else if (id == GUI_ID_SKIN_ANIMATION_FPS)
449 				{
450 					const s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
451 					if (scene::ESNT_ANIMATED_MESH == Model->getType())
452 						((scene::IAnimatedMeshSceneNode*)Model)->setAnimationSpeed((f32)pos);
453 				}
454 				break;
455 
456 			case EGET_COMBO_BOX_CHANGED:
457 
458 				// control anti-aliasing/filtering
459 				if (id == GUI_ID_TEXTUREFILTER)
460 				{
461 					OnTextureFilterSelected( (IGUIComboBox*)event.GUIEvent.Caller );
462 				}
463 				break;
464 
465 			case EGET_BUTTON_CLICKED:
466 
467 				switch(id)
468 				{
469 				case GUI_ID_BUTTON_SET_SCALE:
470 					{
471 						// set scale
472 						gui::IGUIElement* root = env->getRootGUIElement();
473 						core::vector3df scale;
474 						core::stringc s;
475 
476 						s = root->getElementFromId(GUI_ID_X_SCALE, true)->getText();
477 						scale.X = (f32)atof(s.c_str());
478 						s = root->getElementFromId(GUI_ID_Y_SCALE, true)->getText();
479 						scale.Y = (f32)atof(s.c_str());
480 						s = root->getElementFromId(GUI_ID_Z_SCALE, true)->getText();
481 						scale.Z = (f32)atof(s.c_str());
482 
483 						if (Model)
484 							Model->setScale(scale);
485 						updateScaleInfo(Model);
486 					}
487 					break;
488 				case GUI_ID_BUTTON_SCALE_MUL10:
489 					if (Model)
490 						Model->setScale(Model->getScale()*10.f);
491 					updateScaleInfo(Model);
492 					break;
493 				case GUI_ID_BUTTON_SCALE_DIV10:
494 					if (Model)
495 						Model->setScale(Model->getScale()*0.1f);
496 					updateScaleInfo(Model);
497 					break;
498 				case GUI_ID_BUTTON_OPEN_MODEL:
499 					env->addFileOpenDialog(L"Please select a model file to open");
500 					break;
501 				case GUI_ID_BUTTON_SHOW_ABOUT:
502 					showAboutText();
503 					break;
504 				case GUI_ID_BUTTON_SHOW_TOOLBOX:
505 					createToolBox();
506 					break;
507 				case GUI_ID_BUTTON_SELECT_ARCHIVE:
508 					env->addFileOpenDialog(L"Please select your game archive/directory");
509 					break;
510 				}
511 
512 				break;
513 			default:
514 				break;
515 			}
516 		}
517 
518 		return false;
519 	}
520 
521 
522 	/*
523 		Handle key-up events
524 	*/
OnKeyUp(irr::EKEY_CODE keyCode)525 	bool OnKeyUp(irr::EKEY_CODE keyCode)
526 	{
527 		// Don't handle keys if we have a modal dialog open as it would lead
528 		// to unexpected application behaviour for the user.
529 		if ( hasModalDialog() )
530 			return false;
531 
532 		if (keyCode == irr::KEY_ESCAPE)
533 		{
534 			if (Device)
535 			{
536 				scene::ICameraSceneNode * camera =
537 					Device->getSceneManager()->getActiveCamera();
538 				if (camera)
539 				{
540 					camera->setInputReceiverEnabled( !camera->isInputReceiverEnabled() );
541 				}
542 				return true;
543 			}
544 		}
545 		else if (keyCode == irr::KEY_F1)
546 		{
547 			if (Device)
548 			{
549 				IGUIElement* elem = Device->getGUIEnvironment()->getRootGUIElement()->getElementFromId(GUI_ID_POSITION_TEXT);
550 				if (elem)
551 					elem->setVisible(!elem->isVisible());
552 			}
553 		}
554 		else if (keyCode == irr::KEY_KEY_M)
555 		{
556 			if (Device)
557 				Device->minimizeWindow();
558 		}
559 		else if (keyCode == irr::KEY_KEY_L)
560 		{
561 			UseLight=!UseLight;
562 			if (Model)
563 			{
564 				Model->setMaterialFlag(video::EMF_LIGHTING, UseLight);
565 				Model->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, UseLight);
566 			}
567 		}
568 		return false;
569 	}
570 
571 
572 	/*
573 		Handle "menu item clicked" events.
574 	*/
OnMenuItemSelected(IGUIContextMenu * menu)575 	void OnMenuItemSelected( IGUIContextMenu* menu )
576 	{
577 		s32 id = menu->getItemCommandId(menu->getSelectedItem());
578 		IGUIEnvironment* env = Device->getGUIEnvironment();
579 
580 		switch(id)
581 		{
582 		case GUI_ID_OPEN_MODEL: // FilOnButtonSetScalinge -> Open Model
583 			env->addFileOpenDialog(L"Please select a model file to open");
584 			break;
585 		case GUI_ID_SET_MODEL_ARCHIVE: // File -> Set Model Archive
586 			env->addFileOpenDialog(L"Please select your game archive/directory");
587 			break;
588 		case GUI_ID_LOAD_AS_OCTREE: // File -> LoadAsOctree
589 			Octree = !Octree;
590 			menu->setItemChecked(menu->getSelectedItem(), Octree);
591 			break;
592 		case GUI_ID_QUIT: // File -> Quit
593 			Device->closeDevice();
594 			break;
595 		case GUI_ID_SKY_BOX_VISIBLE: // View -> Skybox
596 			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
597 			SkyBox->setVisible(!SkyBox->isVisible());
598 			break;
599 		case GUI_ID_DEBUG_OFF: // View -> Debug Information
600 			menu->setItemChecked(menu->getSelectedItem()+1, false);
601 			menu->setItemChecked(menu->getSelectedItem()+2, false);
602 			menu->setItemChecked(menu->getSelectedItem()+3, false);
603 			menu->setItemChecked(menu->getSelectedItem()+4, false);
604 			menu->setItemChecked(menu->getSelectedItem()+5, false);
605 			menu->setItemChecked(menu->getSelectedItem()+6, false);
606 			if (Model)
607 				Model->setDebugDataVisible(scene::EDS_OFF);
608 			break;
609 		case GUI_ID_DEBUG_BOUNDING_BOX: // View -> Debug Information
610 			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
611 			if (Model)
612 				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_BBOX));
613 			break;
614 		case GUI_ID_DEBUG_NORMALS: // View -> Debug Information
615 			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
616 			if (Model)
617 				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_NORMALS));
618 			break;
619 		case GUI_ID_DEBUG_SKELETON: // View -> Debug Information
620 			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
621 			if (Model)
622 				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_SKELETON));
623 			break;
624 		case GUI_ID_DEBUG_WIRE_OVERLAY: // View -> Debug Information
625 			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
626 			if (Model)
627 				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_MESH_WIRE_OVERLAY));
628 			break;
629 		case GUI_ID_DEBUG_HALF_TRANSPARENT: // View -> Debug Information
630 			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
631 			if (Model)
632 				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_HALF_TRANSPARENCY));
633 			break;
634 		case GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES: // View -> Debug Information
635 			menu->setItemChecked(menu->getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
636 			if (Model)
637 				Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->isDebugDataVisible()^scene::EDS_BBOX_BUFFERS));
638 			break;
639 		case GUI_ID_DEBUG_ALL: // View -> Debug Information
640 			menu->setItemChecked(menu->getSelectedItem()-1, true);
641 			menu->setItemChecked(menu->getSelectedItem()-2, true);
642 			menu->setItemChecked(menu->getSelectedItem()-3, true);
643 			menu->setItemChecked(menu->getSelectedItem()-4, true);
644 			menu->setItemChecked(menu->getSelectedItem()-5, true);
645 			menu->setItemChecked(menu->getSelectedItem()-6, true);
646 			if (Model)
647 				Model->setDebugDataVisible(scene::EDS_FULL);
648 			break;
649 		case GUI_ID_ABOUT: // Help->About
650 			showAboutText();
651 			break;
652 		case GUI_ID_MODEL_MATERIAL_SOLID: // View -> Material -> Solid
653 			if (Model)
654 				Model->setMaterialType(video::EMT_SOLID);
655 			break;
656 		case GUI_ID_MODEL_MATERIAL_TRANSPARENT: // View -> Material -> Transparent
657 			if (Model)
658 				Model->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
659 			break;
660 		case GUI_ID_MODEL_MATERIAL_REFLECTION: // View -> Material -> Reflection
661 			if (Model)
662 				Model->setMaterialType(video::EMT_SPHERE_MAP);
663 			break;
664 
665 		case GUI_ID_CAMERA_MAYA:
666 			setActiveCamera(Camera[0]);
667 			break;
668 		case GUI_ID_CAMERA_FIRST_PERSON:
669 			setActiveCamera(Camera[1]);
670 			break;
671 		}
672 	}
673 
674 	/*
675 		Handle the event that one of the texture-filters was selected in the corresponding combobox.
676 	*/
OnTextureFilterSelected(IGUIComboBox * combo)677 	void OnTextureFilterSelected( IGUIComboBox* combo )
678 	{
679 		s32 pos = combo->getSelected();
680 		switch (pos)
681 		{
682 			case 0:
683 			if (Model)
684 			{
685 				Model->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
686 				Model->setMaterialFlag(video::EMF_TRILINEAR_FILTER, false);
687 				Model->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, false);
688 			}
689 			break;
690 			case 1:
691 			if (Model)
692 			{
693 				Model->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
694 				Model->setMaterialFlag(video::EMF_TRILINEAR_FILTER, false);
695 			}
696 			break;
697 			case 2:
698 			if (Model)
699 			{
700 				Model->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
701 				Model->setMaterialFlag(video::EMF_TRILINEAR_FILTER, true);
702 			}
703 			break;
704 			case 3:
705 			if (Model)
706 			{
707 				Model->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, true);
708 			}
709 			break;
710 			case 4:
711 			if (Model)
712 			{
713 				Model->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, false);
714 			}
715 			break;
716 		}
717 	}
718 };
719 
720 
721 /*
722 Most of the hard work is done. We only need to create the Irrlicht Engine
723 device and all the buttons, menus and toolbars. We start up the engine as
724 usual, using createDevice(). To make our application catch events, we set our
725 eventreceiver as parameter. As you can see, there is also a call to
726 IrrlichtDevice::setResizeable(). This makes the render window resizeable, which
727 is quite useful for a mesh viewer.
728 */
main(int argc,char * argv[])729 int main(int argc, char* argv[])
730 {
731 	// ask user for driver
732 	video::E_DRIVER_TYPE driverType=driverChoiceConsole();
733 	if (driverType==video::EDT_COUNT)
734 		return 1;
735 
736 	// create device and exit if creation failed
737 	MyEventReceiver receiver;
738 	Device = createDevice(driverType, core::dimension2d<u32>(800, 600),
739 		16, false, false, false, &receiver);
740 
741 	if (Device == 0)
742 		return 1; // could not create selected driver.
743 
744 	Device->setResizable(true);
745 
746 	Device->setWindowCaption(L"Irrlicht Engine - Loading...");
747 
748 	video::IVideoDriver* driver = Device->getVideoDriver();
749 	IGUIEnvironment* env = Device->getGUIEnvironment();
750 	scene::ISceneManager* smgr = Device->getSceneManager();
751 	smgr->getParameters()->setAttribute(scene::COLLADA_CREATE_SCENE_INSTANCES, true);
752 
753 	driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
754 
755 	smgr->addLightSceneNode(0, core::vector3df(200,200,200),
756 		video::SColorf(1.0f,1.0f,1.0f),2000);
757 	smgr->setAmbientLight(video::SColorf(0.3f,0.3f,0.3f));
758 	// add our media directory as "search path"
759 	Device->getFileSystem()->addFileArchive("../../media/");
760 
761 	/*
762 	The next step is to read the configuration file. It is stored in the xml
763 	format and looks a little bit like this:
764 
765 	@verbatim
766 	<?xml version="1.0"?>
767 	<config>
768 		<startUpModel file="some filename" />
769 		<messageText caption="Irrlicht Engine Mesh Viewer">
770 			Hello!
771 		</messageText>
772 	</config>
773 	@endverbatim
774 
775 	We need the data stored in there to be written into the global variables
776 	StartUpModelFile, MessageText and Caption. This is now done using the
777 	Irrlicht Engine integrated XML parser:
778 	*/
779 
780 	// read configuration from xml file
781 
782 	io::IXMLReader* xml = Device->getFileSystem()->createXMLReader( L"config.xml");
783 
784 	while(xml && xml->read())
785 	{
786 		switch(xml->getNodeType())
787 		{
788 		case io::EXN_TEXT:
789 			// in this xml file, the only text which occurs is the
790 			// messageText
791 			MessageText = xml->getNodeData();
792 			break;
793 		case io::EXN_ELEMENT:
794 			{
795 				if (core::stringw("startUpModel") == xml->getNodeName())
796 					StartUpModelFile = xml->getAttributeValue(L"file");
797 				else
798 				if (core::stringw("messageText") == xml->getNodeName())
799 					Caption = xml->getAttributeValue(L"caption");
800 			}
801 			break;
802 		default:
803 			break;
804 		}
805 	}
806 
807 	if (xml)
808 		xml->drop(); // don't forget to delete the xml reader
809 
810 	if (argc > 1)
811 		StartUpModelFile = argv[1];
812 
813 	/*
814 	That wasn't difficult. Now we'll set a nicer font and create the Menu.
815 	It is possible to create submenus for every menu item. The call
816 	menu->addItem(L"File", -1, true, true); for example adds a new menu
817 	Item with the name "File" and the id -1. The following parameter says
818 	that the menu item should be enabled, and the last one says, that there
819 	should be a submenu. The submenu can now be accessed with
820 	menu->getSubMenu(0), because the "File" entry is the menu item with
821 	index 0.
822 	*/
823 
824 	// set a nicer font
825 
826 	IGUISkin* skin = env->getSkin();
827 	IGUIFont* font = env->getFont("fonthaettenschweiler.bmp");
828 	if (font)
829 		skin->setFont(font);
830 
831 	// create menu
832 	gui::IGUIContextMenu* menu = env->addMenu();
833 	menu->addItem(L"File", -1, true, true);
834 	menu->addItem(L"View", -1, true, true);
835 	menu->addItem(L"Camera", -1, true, true);
836 	menu->addItem(L"Help", -1, true, true);
837 
838 	gui::IGUIContextMenu* submenu;
839 	submenu = menu->getSubMenu(0);
840 	submenu->addItem(L"Open Model File & Texture...", GUI_ID_OPEN_MODEL);
841 	submenu->addItem(L"Set Model Archive...", GUI_ID_SET_MODEL_ARCHIVE);
842 	submenu->addItem(L"Load as Octree", GUI_ID_LOAD_AS_OCTREE);
843 	submenu->addSeparator();
844 	submenu->addItem(L"Quit", GUI_ID_QUIT);
845 
846 	submenu = menu->getSubMenu(1);
847 	submenu->addItem(L"sky box visible", GUI_ID_SKY_BOX_VISIBLE, true, false, true);
848 	submenu->addItem(L"toggle model debug information", GUI_ID_TOGGLE_DEBUG_INFO, true, true);
849 	submenu->addItem(L"model material", -1, true, true );
850 
851 	submenu = submenu->getSubMenu(1);
852 	submenu->addItem(L"Off", GUI_ID_DEBUG_OFF);
853 	submenu->addItem(L"Bounding Box", GUI_ID_DEBUG_BOUNDING_BOX);
854 	submenu->addItem(L"Normals", GUI_ID_DEBUG_NORMALS);
855 	submenu->addItem(L"Skeleton", GUI_ID_DEBUG_SKELETON);
856 	submenu->addItem(L"Wire overlay", GUI_ID_DEBUG_WIRE_OVERLAY);
857 	submenu->addItem(L"Half-Transparent", GUI_ID_DEBUG_HALF_TRANSPARENT);
858 	submenu->addItem(L"Buffers bounding boxes", GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES);
859 	submenu->addItem(L"All", GUI_ID_DEBUG_ALL);
860 
861 	submenu = menu->getSubMenu(1)->getSubMenu(2);
862 	submenu->addItem(L"Solid", GUI_ID_MODEL_MATERIAL_SOLID);
863 	submenu->addItem(L"Transparent", GUI_ID_MODEL_MATERIAL_TRANSPARENT);
864 	submenu->addItem(L"Reflection", GUI_ID_MODEL_MATERIAL_REFLECTION);
865 
866 	submenu = menu->getSubMenu(2);
867 	submenu->addItem(L"Maya Style", GUI_ID_CAMERA_MAYA);
868 	submenu->addItem(L"First Person", GUI_ID_CAMERA_FIRST_PERSON);
869 
870 	submenu = menu->getSubMenu(3);
871 	submenu->addItem(L"About", GUI_ID_ABOUT);
872 
873 	/*
874 	Below the menu we want a toolbar, onto which we can place colored
875 	buttons and important looking stuff like a senseless combobox.
876 	*/
877 
878 	// create toolbar
879 
880 	gui::IGUIToolBar* bar = env->addToolBar();
881 
882 	video::ITexture* image = driver->getTexture("open.png");
883 	bar->addButton(GUI_ID_BUTTON_OPEN_MODEL, 0, L"Open a model",image, 0, false, true);
884 
885 	image = driver->getTexture("tools.png");
886 	bar->addButton(GUI_ID_BUTTON_SHOW_TOOLBOX, 0, L"Open Toolset",image, 0, false, true);
887 
888 	image = driver->getTexture("zip.png");
889 	bar->addButton(GUI_ID_BUTTON_SELECT_ARCHIVE, 0, L"Set Model Archive",image, 0, false, true);
890 
891 	image = driver->getTexture("help.png");
892 	bar->addButton(GUI_ID_BUTTON_SHOW_ABOUT, 0, L"Open Help", image, 0, false, true);
893 
894 	// create a combobox for texture filters
895 
896 	gui::IGUIComboBox* box = env->addComboBox(core::rect<s32>(250,4,350,23), bar, GUI_ID_TEXTUREFILTER);
897 	box->addItem(L"No filtering");
898 	box->addItem(L"Bilinear");
899 	box->addItem(L"Trilinear");
900 	box->addItem(L"Anisotropic");
901 	box->addItem(L"Isotropic");
902 
903 	/*
904 	To make the editor look a little bit better, we disable transparent gui
905 	elements, and add an Irrlicht Engine logo. In addition, a text showing
906 	the current frames per second value is created and the window caption is
907 	changed.
908 	*/
909 
910 	// disable alpha
911 
912 	for (s32 i=0; i<gui::EGDC_COUNT ; ++i)
913 	{
914 		video::SColor col = env->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
915 		col.setAlpha(255);
916 		env->getSkin()->setColor((gui::EGUI_DEFAULT_COLOR)i, col);
917 	}
918 
919 	// add a tabcontrol
920 
921 	createToolBox();
922 
923 	// create fps text
924 
925 	IGUIStaticText* fpstext = env->addStaticText(L"",
926 			core::rect<s32>(400,4,570,23), true, false, bar);
927 
928 	IGUIStaticText* postext = env->addStaticText(L"",
929 			core::rect<s32>(10,50,470,80),false, false, 0, GUI_ID_POSITION_TEXT);
930 	postext->setVisible(false);
931 
932 	// set window caption
933 
934 	Caption += " - [";
935 	Caption += driver->getName();
936 	Caption += "]";
937 	Device->setWindowCaption(Caption.c_str());
938 
939 	/*
940 	That's nearly the whole application. We simply show the about message
941 	box at start up, and load the first model. To make everything look
942 	better, a skybox is created and a user controlled camera, to make the
943 	application a little bit more interactive. Finally, everything is drawn
944 	in a standard drawing loop.
945 	*/
946 
947 	// show about message box and load default model
948 	if (argc==1)
949 		showAboutText();
950 	loadModel(StartUpModelFile.c_str());
951 
952 	// add skybox
953 
954 	SkyBox = smgr->addSkyBoxSceneNode(
955 		driver->getTexture("irrlicht2_up.jpg"),
956 		driver->getTexture("irrlicht2_dn.jpg"),
957 		driver->getTexture("irrlicht2_lf.jpg"),
958 		driver->getTexture("irrlicht2_rt.jpg"),
959 		driver->getTexture("irrlicht2_ft.jpg"),
960 		driver->getTexture("irrlicht2_bk.jpg"));
961 
962 	// add a camera scene node
963 	Camera[0] = smgr->addCameraSceneNodeMaya();
964 	Camera[0]->setFarValue(20000.f);
965 	// Maya cameras reposition themselves relative to their target, so target the location
966 	// where the mesh scene node is placed.
967 	Camera[0]->setTarget(core::vector3df(0,30,0));
968 
969 	Camera[1] = smgr->addCameraSceneNodeFPS();
970 	Camera[1]->setFarValue(20000.f);
971 	Camera[1]->setPosition(core::vector3df(0,0,-70));
972 	Camera[1]->setTarget(core::vector3df(0,30,0));
973 
974 	setActiveCamera(Camera[0]);
975 
976 	// load the irrlicht engine logo
977 	IGUIImage *img =
978 		env->addImage(driver->getTexture("irrlichtlogo2.png"),
979 			core::position2d<s32>(10, driver->getScreenSize().Height - 128));
980 
981 	// lock the logo's edges to the bottom left corner of the screen
982 	img->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT,
983 			EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
984 
985 	// remember state so we notice when the window does lose the focus
986 	bool hasFocus = Device->isWindowFocused();
987 
988 	// draw everything
989 
990 	while(Device->run() && driver)
991 	{
992 		// Catch focus changes (workaround until Irrlicht has events for this)
993 		bool focused = Device->isWindowFocused();
994 		if ( hasFocus && !focused )
995 			onKillFocus();
996 		hasFocus = focused;
997 
998 		if (Device->isWindowActive())
999 		{
1000 			driver->beginScene(true, true, video::SColor(150,50,50,50));
1001 
1002 			smgr->drawAll();
1003 			env->drawAll();
1004 
1005 			driver->endScene();
1006 
1007 			// update information about current frame-rate
1008 			core::stringw str(L"FPS: ");
1009 			str.append(core::stringw(driver->getFPS()));
1010 			str += L" Tris: ";
1011 			str.append(core::stringw(driver->getPrimitiveCountDrawn()));
1012 			fpstext->setText(str.c_str());
1013 
1014 			// update information about the active camera
1015 			scene::ICameraSceneNode* cam = Device->getSceneManager()->getActiveCamera();
1016 			str = L"Pos: ";
1017 			str.append(core::stringw(cam->getPosition().X));
1018 			str += L" ";
1019 			str.append(core::stringw(cam->getPosition().Y));
1020 			str += L" ";
1021 			str.append(core::stringw(cam->getPosition().Z));
1022 			str += L" Tgt: ";
1023 			str.append(core::stringw(cam->getTarget().X));
1024 			str += L" ";
1025 			str.append(core::stringw(cam->getTarget().Y));
1026 			str += L" ";
1027 			str.append(core::stringw(cam->getTarget().Z));
1028 			postext->setText(str.c_str());
1029 
1030 			// update the tool dialog
1031 			updateToolBox();
1032 		}
1033 		else
1034 			Device->yield();
1035 	}
1036 
1037 	Device->drop();
1038 	return 0;
1039 }
1040 
1041 /*
1042 **/
1043