1 /** Example 021 Quake3 Explorer
2 
3 This Tutorial shows how to load different Quake 3 maps.
4 
5 Features:
6 	- Load BSP Archives at Runtime from the menu
7 	- Load a Map from the menu. Showing with Screenshot
8 	- Set the VideoDriver at runtime from menu
9 	- Adjust GammaLevel at runtime
10 	- Create SceneNodes for the Shaders
11 	- Load EntityList and create Entity SceneNodes
12 	- Create Players with Weapons and with Collision Response
13 	- Play music
14 
15 You can download the Quake III Arena demo ( copyright id software )
16 at the following location:
17 ftp://ftp.idsoftware.com/idstuff/quake3/win32/q3ademo.exe
18 
19 Copyright 2006-2011 Burningwater, Thomas Alten
20 */
21 
22 #include "driverChoice.h"
23 #include <irrlicht.h>
24 #include "q3factory.h"
25 #include "sound.h"
26 
27 /*
28 	Game Data is used to hold Data which is needed to drive the game
29 */
30 struct GameData
31 {
GameDataGameData32 	GameData ( const path &startupDir) :
33 		retVal(0), StartupDir(startupDir), createExDevice(0), Device(0)
34 	{
35 		setDefault ();
36 	}
37 
38 	void setDefault ();
39 	s32 save ( const path &filename );
40 	s32 load ( const path &filename );
41 
42 	s32 debugState;
43 	s32 gravityState;
44 	s32 flyTroughState;
45 	s32 wireFrame;
46 	s32 guiActive;
47 	s32 guiInputActive;
48 	f32 GammaValue;
49 	s32 retVal;
50 	s32 sound;
51 
52 	path StartupDir;
53 	stringw CurrentMapName;
54 	array<path> CurrentArchiveList;
55 
56 	vector3df PlayerPosition;
57 	vector3df PlayerRotation;
58 
59 	tQ3EntityList Variable;
60 
61 	Q3LevelLoadParameter loadParam;
62 	SIrrlichtCreationParameters deviceParam;
63 	funcptr_createDeviceEx createExDevice;
64 	IrrlichtDevice *Device;
65 };
66 
67 /*
68 	set default settings
69 */
setDefault()70 void GameData::setDefault ()
71 {
72 	debugState = EDS_OFF;
73 	gravityState = 1;
74 	flyTroughState = 0;
75 	wireFrame = 0;
76 	guiActive = 1;
77 	guiInputActive = 0;
78 	GammaValue = 1.f;
79 
80 	// default deviceParam;
81 #if defined ( _IRR_WINDOWS_ )
82 	deviceParam.DriverType = EDT_DIRECT3D9;
83 #else
84 	deviceParam.DriverType = EDT_OPENGL;
85 #endif
86 	deviceParam.WindowSize.Width = 800;
87 	deviceParam.WindowSize.Height = 600;
88 	deviceParam.Fullscreen = false;
89 	deviceParam.Bits = 24;
90 	deviceParam.ZBufferBits = 16;
91 	deviceParam.Vsync = false;
92 	deviceParam.AntiAlias = false;
93 
94 	// default Quake3 loadParam
95 	loadParam.defaultLightMapMaterial = EMT_LIGHTMAP;
96 	loadParam.defaultModulate = EMFN_MODULATE_1X;
97 	loadParam.defaultFilter = EMF_ANISOTROPIC_FILTER;
98 	loadParam.verbose = 2;
99 	loadParam.mergeShaderBuffer = 1;		// merge meshbuffers with same material
100 	loadParam.cleanUnResolvedMeshes = 1;	// should unresolved meshes be cleaned. otherwise blue texture
101 	loadParam.loadAllShaders = 1;			// load all scripts in the script directory
102 	loadParam.loadSkyShader = 0;			// load sky Shader
103 	loadParam.alpharef = 1;
104 
105 	sound = 0;
106 
107 	CurrentMapName = "";
108 	CurrentArchiveList.clear ();
109 
110 	// Explorer Media directory
111 	CurrentArchiveList.push_back ( StartupDir + "../../media/" );
112 
113 	// Add the original quake3 files before you load your custom map
114 	// Most mods are using the original shaders, models&items&weapons
115 	CurrentArchiveList.push_back("/q/baseq3/");
116 
117 	CurrentArchiveList.push_back(StartupDir + "../../media/map-20kdm2.pk3");
118 }
119 
120 /*
121 	Load the current game State from a typical quake3 cfg file
122 */
load(const path & filename)123 s32 GameData::load ( const path &filename )
124 {
125 	if (!Device)
126 		return 0;
127 
128 	// the quake3 mesh loader can also handle *.shader and *.cfg file
129 	IQ3LevelMesh* mesh = (IQ3LevelMesh*) Device->getSceneManager()->getMesh ( filename );
130 	if (!mesh)
131 		return 0;
132 
133 	tQ3EntityList &entityList = mesh->getEntityList ();
134 
135 	stringc s;
136 	u32 pos;
137 
138 	for ( u32 e = 0; e != entityList.size (); ++e )
139 	{
140 		//dumpShader ( s, &entityList[e], false );
141 		//printf ( s.c_str () );
142 
143 		for ( u32 g = 0; g != entityList[e].getGroupSize (); ++g )
144 		{
145 			const SVarGroup *group = entityList[e].getGroup ( g );
146 
147 			for ( u32 index = 0; index < group->Variable.size (); ++index )
148 			{
149 				const SVariable &v = group->Variable[index];
150 				pos = 0;
151 				if ( v.name == "playerposition" )
152 				{
153 					PlayerPosition = getAsVector3df ( v.content, pos );
154 				}
155 				else
156 				if ( v.name == "playerrotation" )
157 				{
158 					PlayerRotation = getAsVector3df ( v.content, pos );
159 				}
160 			}
161 		}
162 	}
163 
164 	return 1;
165 }
166 
167 /*
168 	Store the current game State in a quake3 configuration file
169 */
save(const path & filename)170 s32 GameData::save ( const path &filename )
171 {
172 	return 0;
173 	if (!Device)
174 		return 0;
175 
176 	c8 buf[128];
177 	u32 i;
178 
179 	// Store current Archive for restart
180 	CurrentArchiveList.clear();
181 	IFileSystem *fs = Device->getFileSystem();
182 	for ( i = 0; i != fs->getFileArchiveCount(); ++i )
183 	{
184 		CurrentArchiveList.push_back ( fs->getFileArchive(i)->getFileList()->getPath() );
185 	}
186 
187 	// Store Player Position and Rotation
188 	ICameraSceneNode * camera = Device->getSceneManager()->getActiveCamera ();
189 	if ( camera )
190 	{
191 		PlayerPosition = camera->getPosition ();
192 		PlayerRotation = camera->getRotation ();
193 	}
194 
195 	IWriteFile *file = fs->createAndWriteFile ( filename );
196 	if (!file)
197 		return 0;
198 
199 	snprintf ( buf, 128, "playerposition %.f %.f %.f\nplayerrotation %.f %.f %.f\n",
200 			PlayerPosition.X, PlayerPosition.Z, PlayerPosition.Y,
201 			PlayerRotation.X, PlayerRotation.Z, PlayerRotation.Y);
202 	file->write ( buf, (s32) strlen ( buf ) );
203 	for ( i = 0; i != fs->getFileArchiveCount(); ++i )
204 	{
205 		snprintf ( buf, 128, "archive %s\n",stringc ( fs->getFileArchive(i)->getFileList()->getPath() ).c_str () );
206 		file->write ( buf, (s32) strlen ( buf ) );
207 	}
208 
209 	file->drop ();
210 	return 1;
211 }
212 
213 /*
214 	Representing a player
215 */
216 struct Q3Player : public IAnimationEndCallBack
217 {
Q3PlayerQ3Player218 	Q3Player ()
219 	: Device(0), MapParent(0), Mesh(0), WeaponNode(0), StartPositionCurrent(0)
220 	{
221 		animation[0] = 0;
222 		memset(Anim, 0, sizeof(TimeFire)*4);
223 	}
224 
225 	virtual void OnAnimationEnd(IAnimatedMeshSceneNode* node);
226 
227 	void create (	IrrlichtDevice *device,
228 					IQ3LevelMesh* mesh,
229 					ISceneNode *mapNode,
230 					IMetaTriangleSelector *meta
231 				);
232 	void shutdown ();
233 	void setAnim ( const c8 *name );
234 	void respawn ();
235 	void setpos ( const vector3df &pos, const vector3df& rotation );
236 
camQ3Player237 	ISceneNodeAnimatorCollisionResponse * cam() { return camCollisionResponse ( Device ); }
238 
239 	IrrlichtDevice *Device;
240 	ISceneNode* MapParent;
241 	IQ3LevelMesh* Mesh;
242 	IAnimatedMeshSceneNode* WeaponNode;
243 	s32 StartPositionCurrent;
244 	TimeFire Anim[4];
245 	c8 animation[64];
246 	c8 buf[64];
247 };
248 
249 
250 /* End player
251 */
shutdown()252 void Q3Player::shutdown ()
253 {
254 	setAnim ( 0 );
255 
256 	dropElement (WeaponNode);
257 
258 	if ( Device )
259 	{
260 		ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();
261 		dropElement ( camera );
262 		Device = 0;
263 	}
264 
265 	MapParent = 0;
266 	Mesh = 0;
267 }
268 
269 
270 /* create a new player
271 */
create(IrrlichtDevice * device,IQ3LevelMesh * mesh,ISceneNode * mapNode,IMetaTriangleSelector * meta)272 void Q3Player::create ( IrrlichtDevice *device, IQ3LevelMesh* mesh, ISceneNode *mapNode, IMetaTriangleSelector *meta )
273 {
274 	setTimeFire ( Anim + 0, 200, FIRED );
275 	setTimeFire ( Anim + 1, 5000 );
276 
277 	if (!device)
278 		return;
279 	// load FPS weapon to Camera
280 	Device = device;
281 	Mesh = mesh;
282 	MapParent = mapNode;
283 
284 	ISceneManager *smgr = device->getSceneManager ();
285 	IVideoDriver * driver = device->getVideoDriver();
286 
287 	ICameraSceneNode* camera = 0;
288 
289 	SKeyMap keyMap[10];
290 	keyMap[0].Action = EKA_MOVE_FORWARD;
291 	keyMap[0].KeyCode = KEY_UP;
292 	keyMap[1].Action = EKA_MOVE_FORWARD;
293 	keyMap[1].KeyCode = KEY_KEY_W;
294 
295 	keyMap[2].Action = EKA_MOVE_BACKWARD;
296 	keyMap[2].KeyCode = KEY_DOWN;
297 	keyMap[3].Action = EKA_MOVE_BACKWARD;
298 	keyMap[3].KeyCode = KEY_KEY_S;
299 
300 	keyMap[4].Action = EKA_STRAFE_LEFT;
301 	keyMap[4].KeyCode = KEY_LEFT;
302 	keyMap[5].Action = EKA_STRAFE_LEFT;
303 	keyMap[5].KeyCode = KEY_KEY_A;
304 
305 	keyMap[6].Action = EKA_STRAFE_RIGHT;
306 	keyMap[6].KeyCode = KEY_RIGHT;
307 	keyMap[7].Action = EKA_STRAFE_RIGHT;
308 	keyMap[7].KeyCode = KEY_KEY_D;
309 
310 	keyMap[8].Action = EKA_JUMP_UP;
311 	keyMap[8].KeyCode = KEY_KEY_J;
312 
313 	keyMap[9].Action = EKA_CROUCH;
314 	keyMap[9].KeyCode = KEY_KEY_C;
315 
316 	camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 0.6f, -1, keyMap, 10, false, 0.6f);
317 	camera->setName ( "First Person Camera" );
318 	//camera->setFOV ( 100.f * core::DEGTORAD );
319 	camera->setFarValue( 20000.f );
320 
321 	IAnimatedMeshMD2* weaponMesh = (IAnimatedMeshMD2*) smgr->getMesh("gun.md2");
322 	if ( 0 == weaponMesh )
323 		return;
324 
325 	if ( weaponMesh->getMeshType() == EAMT_MD2 )
326 	{
327 		s32 count = weaponMesh->getAnimationCount();
328 		for ( s32 i = 0; i != count; ++i )
329 		{
330 			snprintf ( buf, 64, "Animation: %s", weaponMesh->getAnimationName(i) );
331 			device->getLogger()->log(buf, ELL_INFORMATION);
332 		}
333 	}
334 
335 	WeaponNode = smgr->addAnimatedMeshSceneNode(
336 						weaponMesh,
337 						smgr->getActiveCamera(),
338 						10,
339 						vector3df( 0, 0, 0),
340 						vector3df(-90,-90,90)
341 						);
342 	WeaponNode->setMaterialFlag(EMF_LIGHTING, false);
343 	WeaponNode->setMaterialTexture(0, driver->getTexture( "gun.jpg"));
344 	WeaponNode->setLoopMode ( false );
345 	WeaponNode->setName ( "tommi the gun man" );
346 
347 	//create a collision auto response animator
348 	ISceneNodeAnimator* anim =
349 		smgr->createCollisionResponseAnimator( meta, camera,
350 			vector3df(30,45,30),
351 			getGravity ( "earth" ),
352 			vector3df(0,40,0),
353 			0.0005f
354 		);
355 
356 	camera->addAnimator( anim );
357 	anim->drop();
358 
359 	if ( meta )
360 	{
361 		meta->drop ();
362 	}
363 
364 	respawn ();
365 	setAnim ( "idle" );
366 }
367 
368 
369 /*
370 	so we need a good starting Position in the level.
371 	we can ask the Quake3 Loader for all entities with class_name "info_player_deathmatch"
372 */
respawn()373 void Q3Player::respawn ()
374 {
375 	if (!Device)
376 		return;
377 	ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();
378 
379 	Device->getLogger()->log( "respawn" );
380 
381 	if ( StartPositionCurrent >= Q3StartPosition (
382 			Mesh, camera,StartPositionCurrent++,
383 			cam ()->getEllipsoidTranslation() )
384 		)
385 	{
386 		StartPositionCurrent = 0;
387 	}
388 }
389 
390 /*
391 	set Player position from saved coordinates
392 */
setpos(const vector3df & pos,const vector3df & rotation)393 void Q3Player::setpos ( const vector3df &pos, const vector3df &rotation )
394 {
395 	if (!Device)
396 		return;
397 	Device->getLogger()->log( "setpos" );
398 
399 	ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();
400 	if ( camera )
401 	{
402 		camera->setPosition ( pos );
403 		camera->setRotation ( rotation );
404 		//! New. FPSCamera and animators catches reset on animate 0
405 		camera->OnAnimate ( 0 );
406 	}
407 }
408 
409 /* set the Animation of the player and weapon
410 */
setAnim(const c8 * name)411 void Q3Player::setAnim ( const c8 *name )
412 {
413 	if ( name )
414 	{
415 		snprintf ( animation, 64, "%s", name );
416 		if ( WeaponNode )
417 		{
418 			WeaponNode->setAnimationEndCallback ( this );
419 			WeaponNode->setMD2Animation ( animation );
420 		}
421 	}
422 	else
423 	{
424 		animation[0] = 0;
425 		if ( WeaponNode )
426 		{
427 			WeaponNode->setAnimationEndCallback ( 0 );
428 		}
429 	}
430 }
431 
432 
433 // Callback
OnAnimationEnd(IAnimatedMeshSceneNode * node)434 void Q3Player::OnAnimationEnd(IAnimatedMeshSceneNode* node)
435 {
436 	setAnim ( 0 );
437 }
438 
439 
440 
441 /* GUI Elements
442 */
443 struct GUI
444 {
GUIGUI445 	GUI ()
446 	{
447 		memset ( this, 0, sizeof ( *this ) );
448 	}
449 
dropGUI450 	void drop()
451 	{
452 		dropElement ( Window );
453 		dropElement ( Logo );
454 	}
455 
456 	IGUIComboBox* VideoDriver;
457 	IGUIComboBox* VideoMode;
458 	IGUICheckBox* FullScreen;
459 	IGUICheckBox* Bit32;
460 	IGUIScrollBar* MultiSample;
461 	IGUIButton* SetVideoMode;
462 
463 	IGUIScrollBar* Tesselation;
464 	IGUIScrollBar* Gamma;
465 	IGUICheckBox* Collision;
466 	IGUICheckBox* Visible_Map;
467 	IGUICheckBox* Visible_Shader;
468 	IGUICheckBox* Visible_Fog;
469 	IGUICheckBox* Visible_Unresolved;
470 	IGUICheckBox* Visible_Skydome;
471 	IGUIButton* Respawn;
472 
473 	IGUITable* ArchiveList;
474 	IGUIButton* ArchiveAdd;
475 	IGUIButton* ArchiveRemove;
476 	IGUIFileOpenDialog* ArchiveFileOpen;
477 	IGUIButton* ArchiveUp;
478 	IGUIButton* ArchiveDown;
479 
480 	IGUIListBox* MapList;
481 	IGUITreeView* SceneTree;
482 	IGUIStaticText* StatusLine;
483 	IGUIImage* Logo;
484 	IGUIWindow* Window;
485 };
486 
487 
488 /*
489 	CQuake3EventHandler controls the game
490 */
491 class CQuake3EventHandler : public IEventReceiver
492 {
493 public:
494 
495 	CQuake3EventHandler( GameData *gameData );
496 	virtual ~CQuake3EventHandler ();
497 
498 	void Animate();
499 	void Render();
500 
501 	void AddArchive ( const path& archiveName );
502 	void LoadMap ( const stringw& mapName, s32 collision );
503 	void CreatePlayers();
504 	void AddSky( u32 dome, const c8 *texture );
GetPlayer(u32 index)505 	Q3Player *GetPlayer ( u32 index ) { return &Player[index]; }
506 
507 	void CreateGUI();
508 	void SetGUIActive( s32 command);
509 
510 	bool OnEvent(const SEvent& eve);
511 
512 
513 private:
514 
515 	GameData *Game;
516 
517 	IQ3LevelMesh* Mesh;
518 	ISceneNode* MapParent;
519 	ISceneNode* ShaderParent;
520 	ISceneNode* ItemParent;
521 	ISceneNode* UnresolvedParent;
522 	ISceneNode* BulletParent;
523 	ISceneNode* FogParent;
524 	ISceneNode * SkyNode;
525 	IMetaTriangleSelector *Meta;
526 
527 	c8 buf[256];
528 
529 	Q3Player Player[2];
530 
531 	struct SParticleImpact
532 	{
533 		u32 when;
534 		vector3df pos;
535 		vector3df outVector;
536 	};
537 	array<SParticleImpact> Impacts;
538 	void useItem( Q3Player * player);
539 	void createParticleImpacts( u32 now );
540 
541 	void createTextures ();
542 	void addSceneTreeItem( ISceneNode * parent, IGUITreeViewNode* nodeParent);
543 
544 	GUI gui;
545 	void dropMap ();
546 };
547 
548 /* Constructor
549 */
CQuake3EventHandler(GameData * game)550 CQuake3EventHandler::CQuake3EventHandler( GameData *game )
551 : Game(game), Mesh(0), MapParent(0), ShaderParent(0), ItemParent(0), UnresolvedParent(0),
552 	BulletParent(0), FogParent(0), SkyNode(0), Meta(0)
553 {
554 	buf[0]=0;
555 	// Also use 16 Bit Textures for 16 Bit RenderDevice
556 	if ( Game->deviceParam.Bits == 16 )
557 	{
558 		game->Device->getVideoDriver()->setTextureCreationFlag(ETCF_ALWAYS_16_BIT, true);
559 	}
560 
561 	// Quake3 Shader controls Z-Writing
562 	game->Device->getSceneManager()->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
563 
564 	// create internal textures
565 	createTextures ();
566 
567 	sound_init ( game->Device );
568 
569 	Game->Device->setEventReceiver ( this );
570 }
571 
572 
573 // destructor
~CQuake3EventHandler()574 CQuake3EventHandler::~CQuake3EventHandler ()
575 {
576 	Player[0].shutdown ();
577 	sound_shutdown ();
578 
579 	Game->save( "explorer.cfg" );
580 
581 	Game->Device->drop();
582 }
583 
584 
585 // create runtime textures smog, fog
createTextures()586 void CQuake3EventHandler::createTextures()
587 {
588 	IVideoDriver * driver = Game->Device->getVideoDriver();
589 
590 	dimension2du dim(64, 64);
591 
592 	video::IImage* image;
593 	u32 i;
594 	u32 x;
595 	u32 y;
596 	u32 * data;
597 	for ( i = 0; i != 8; ++i )
598 	{
599 		image = driver->createImage ( video::ECF_A8R8G8B8, dim);
600 		data = (u32*) image->lock ();
601 		for ( y = 0; y != dim.Height; ++y )
602 		{
603 			for ( x = 0; x != dim.Width; ++x )
604 			{
605 				data [x] = 0xFFFFFFFF;
606 			}
607 			data = (u32*) ( (u8*) data + image->getPitch() );
608 		}
609 		image->unlock();
610 		snprintf ( buf, 64, "smoke_%02d", i );
611 		driver->addTexture( buf, image );
612 		image->drop ();
613 	}
614 
615 	// fog
616 	for ( i = 0; i != 1; ++i )
617 	{
618 		image = driver->createImage ( video::ECF_A8R8G8B8, dim);
619 		data = (u32*) image->lock ();
620 		for ( y = 0; y != dim.Height; ++y )
621 		{
622 			for ( x = 0; x != dim.Width; ++x )
623 			{
624 				data [x] = 0xFFFFFFFF;
625 			}
626 			data = (u32*) ( (u8*) data + image->getPitch() );
627 		}
628 		image->unlock();
629 		snprintf ( buf, 64, "fog_%02d", i );
630 		driver->addTexture( buf, image );
631 		image->drop ();
632 	}
633 }
634 
635 
636 /*
637 	create the GUI
638 */
CreateGUI()639 void CQuake3EventHandler::CreateGUI()
640 {
641 
642 	IGUIEnvironment *env = Game->Device->getGUIEnvironment();
643 	IVideoDriver * driver = Game->Device->getVideoDriver();
644 
645 	gui.drop();
646 
647 	// set skin font
648 	IGUIFont* font = env->getFont("fontlucida.png");
649 	if (font)
650 		env->getSkin()->setFont(font);
651 	env->getSkin()->setColor ( EGDC_BUTTON_TEXT, video::SColor(240,0xAA,0xAA,0xAA) );
652 	env->getSkin()->setColor ( EGDC_3D_HIGH_LIGHT, video::SColor(240,0x22,0x22,0x22) );
653 	env->getSkin()->setColor ( EGDC_3D_FACE, video::SColor(240,0x44,0x44,0x44) );
654 	env->getSkin()->setColor ( EGDC_EDITABLE, video::SColor(240,0x44,0x44,0x44) );
655 	env->getSkin()->setColor ( EGDC_FOCUSED_EDITABLE, video::SColor(240,0x54,0x54,0x54) );
656 	env->getSkin()->setColor ( EGDC_WINDOW, video::SColor(240,0x66,0x66,0x66) );
657 
658 	// minimal gui size 800x600
659 	dimension2d<u32> dim ( 800, 600 );
660 	dimension2d<u32> vdim ( Game->Device->getVideoDriver()->getScreenSize() );
661 
662 	if ( vdim.Height >= dim.Height && vdim.Width >= dim.Width )
663 	{
664 		//dim = vdim;
665 	}
666 	else
667 	{
668 	}
669 
670 	gui.Window = env->addWindow ( rect<s32> ( 0, 0, dim.Width, dim.Height ), false, L"Quake3 Explorer" );
671 	gui.Window->setToolTipText ( L"Quake3Explorer. Loads and show various BSP File Format and Shaders." );
672 	gui.Window->getCloseButton()->setToolTipText ( L"Quit Quake3 Explorer" );
673 
674 	// add a status line help text
675 	gui.StatusLine = env->addStaticText( 0, rect<s32>( 5,dim.Height - 30,dim.Width - 5,dim.Height - 10),
676 								false, false, gui.Window, -1, true
677 							);
678 
679 
680 	env->addStaticText ( L"VideoDriver:", rect<s32>( dim.Width - 400, 24, dim.Width - 310, 40 ),false, false, gui.Window, -1, false );
681 	gui.VideoDriver = env->addComboBox(rect<s32>( dim.Width - 300, 24, dim.Width - 10, 40 ),gui.Window);
682 	gui.VideoDriver->addItem(L"Direct3D 9.0c", EDT_DIRECT3D9 );
683 	gui.VideoDriver->addItem(L"Direct3D 8.1", EDT_DIRECT3D8 );
684 	gui.VideoDriver->addItem(L"OpenGL 1.5", EDT_OPENGL);
685 	gui.VideoDriver->addItem(L"Software Renderer", EDT_SOFTWARE);
686 	gui.VideoDriver->addItem(L"Burning's Video (TM) Thomas Alten", EDT_BURNINGSVIDEO);
687 	gui.VideoDriver->setSelected ( gui.VideoDriver->getIndexForItemData ( Game->deviceParam.DriverType ) );
688 	gui.VideoDriver->setToolTipText ( L"Use a VideoDriver" );
689 
690 	env->addStaticText ( L"VideoMode:", rect<s32>( dim.Width - 400, 44, dim.Width - 310, 60 ),false, false, gui.Window, -1, false );
691 	gui.VideoMode = env->addComboBox(rect<s32>( dim.Width - 300, 44, dim.Width - 10, 60 ),gui.Window);
692 	gui.VideoMode->setToolTipText ( L"Supported Screenmodes" );
693 	IVideoModeList *modeList = Game->Device->getVideoModeList();
694 	if ( modeList )
695 	{
696 		s32 i;
697 		for ( i = 0; i != modeList->getVideoModeCount (); ++i )
698 		{
699 			u16 d = modeList->getVideoModeDepth ( i );
700 			if ( d < 16 )
701 				continue;
702 
703 			u16 w = modeList->getVideoModeResolution ( i ).Width;
704 			u16 h = modeList->getVideoModeResolution ( i ).Height;
705 			u32 val = w << 16 | h;
706 
707 			if ( gui.VideoMode->getIndexForItemData ( val ) >= 0 )
708 				continue;
709 
710 			f32 aspect = (f32) w / (f32) h;
711 			const c8 *a = "";
712 			if ( core::equals ( aspect, 1.3333333333f ) ) a = "4:3";
713 			else if ( core::equals ( aspect, 1.6666666f ) ) a = "15:9 widescreen";
714 			else if ( core::equals ( aspect, 1.7777777f ) ) a = "16:9 widescreen";
715 			else if ( core::equals ( aspect, 1.6f ) ) a = "16:10 widescreen";
716 			else if ( core::equals ( aspect, 2.133333f ) ) a = "20:9 widescreen";
717 
718 			snprintf ( buf, sizeof ( buf ), "%d x %d, %s",w, h, a );
719 			gui.VideoMode->addItem ( stringw ( buf ).c_str(), val );
720 		}
721 	}
722 	gui.VideoMode->setSelected ( gui.VideoMode->getIndexForItemData (
723 									Game->deviceParam.WindowSize.Width << 16 |
724 									Game->deviceParam.WindowSize.Height ) );
725 
726 	gui.FullScreen = env->addCheckBox ( Game->deviceParam.Fullscreen, rect<s32>( dim.Width - 400, 64, dim.Width - 300, 80 ), gui.Window,-1, L"Fullscreen" );
727 	gui.FullScreen->setToolTipText ( L"Set Fullscreen or Window Mode" );
728 
729 	gui.Bit32 = env->addCheckBox ( Game->deviceParam.Bits == 32, rect<s32>( dim.Width - 300, 64, dim.Width - 240, 80 ), gui.Window,-1, L"32Bit" );
730 	gui.Bit32->setToolTipText ( L"Use 16 or 32 Bit" );
731 
732 	env->addStaticText ( L"MultiSample:", rect<s32>( dim.Width - 235, 64, dim.Width - 150, 80 ),false, false, gui.Window, -1, false );
733 	gui.MultiSample = env->addScrollBar( true, rect<s32>( dim.Width - 150, 64, dim.Width - 70, 80 ), gui.Window,-1 );
734 	gui.MultiSample->setMin ( 0 );
735 	gui.MultiSample->setMax ( 8 );
736 	gui.MultiSample->setSmallStep ( 1 );
737 	gui.MultiSample->setLargeStep ( 1 );
738 	gui.MultiSample->setPos ( Game->deviceParam.AntiAlias );
739 	gui.MultiSample->setToolTipText ( L"Set the MultiSample (disable, 1x, 2x, 4x, 8x )" );
740 
741 	gui.SetVideoMode = env->addButton (rect<s32>( dim.Width - 60, 64, dim.Width - 10, 80 ), gui.Window, -1,L"set" );
742 	gui.SetVideoMode->setToolTipText ( L"Set Video Mode with current values" );
743 
744 	env->addStaticText ( L"Gamma:", rect<s32>( dim.Width - 400, 104, dim.Width - 310, 120 ),false, false, gui.Window, -1, false );
745 	gui.Gamma = env->addScrollBar( true, rect<s32>( dim.Width - 300, 104, dim.Width - 10, 120 ), gui.Window,-1 );
746 	gui.Gamma->setMin ( 50 );
747 	gui.Gamma->setMax ( 350 );
748 	gui.Gamma->setSmallStep ( 1 );
749 	gui.Gamma->setLargeStep ( 10 );
750 	gui.Gamma->setPos ( core::floor32 ( Game->GammaValue * 100.f ) );
751 	gui.Gamma->setToolTipText ( L"Adjust Gamma Ramp ( 0.5 - 3.5)" );
752 	Game->Device->setGammaRamp ( Game->GammaValue, Game->GammaValue, Game->GammaValue, 0.f, 0.f );
753 
754 
755 	env->addStaticText ( L"Tesselation:", rect<s32>( dim.Width - 400, 124, dim.Width - 310, 140 ),false, false, gui.Window, -1, false );
756 	gui.Tesselation = env->addScrollBar( true, rect<s32>( dim.Width - 300, 124, dim.Width - 10, 140 ), gui.Window,-1 );
757 	gui.Tesselation->setMin ( 2 );
758 	gui.Tesselation->setMax ( 12 );
759 	gui.Tesselation->setSmallStep ( 1 );
760 	gui.Tesselation->setLargeStep ( 1 );
761 	gui.Tesselation->setPos ( Game->loadParam.patchTesselation );
762 	gui.Tesselation->setToolTipText ( L"How smooth should curved surfaces be rendered" );
763 
764 	gui.Collision = env->addCheckBox ( true, rect<s32>( dim.Width - 400, 150, dim.Width - 300, 166 ), gui.Window,-1, L"Collision" );
765 	gui.Collision->setToolTipText ( L"Set collision on or off ( flythrough ). \nPress F7 on your Keyboard" );
766 	gui.Visible_Map = env->addCheckBox ( true, rect<s32>( dim.Width - 300, 150, dim.Width - 240, 166 ), gui.Window,-1, L"Map" );
767 	gui.Visible_Map->setToolTipText ( L"Show or not show the static part the Level. \nPress F3 on your Keyboard" );
768 	gui.Visible_Shader = env->addCheckBox ( true, rect<s32>( dim.Width - 240, 150, dim.Width - 170, 166 ), gui.Window,-1, L"Shader" );
769 	gui.Visible_Shader->setToolTipText ( L"Show or not show the Shader Nodes. \nPress F4 on your Keyboard" );
770 	gui.Visible_Fog = env->addCheckBox ( true, rect<s32>( dim.Width - 170, 150, dim.Width - 110, 166 ), gui.Window,-1, L"Fog" );
771 	gui.Visible_Fog->setToolTipText ( L"Show or not show the Fog Nodes. \nPress F5 on your Keyboard" );
772 	gui.Visible_Unresolved = env->addCheckBox ( true, rect<s32>( dim.Width - 110, 150, dim.Width - 10, 166 ), gui.Window,-1, L"Unresolved" );
773 	gui.Visible_Unresolved->setToolTipText ( L"Show the or not show the Nodes the Engine can't handle. \nPress F6 on your Keyboard" );
774 	gui.Visible_Skydome = env->addCheckBox ( true, rect<s32>( dim.Width - 110, 180, dim.Width - 10, 196 ), gui.Window,-1, L"Skydome" );
775 	gui.Visible_Skydome->setToolTipText ( L"Show the or not show the Skydome." );
776 
777 	//Respawn = env->addButton ( rect<s32>( dim.Width - 260, 90, dim.Width - 10, 106 ), 0,-1, L"Respawn" );
778 
779 	env->addStaticText ( L"Archives:", rect<s32>( 5, dim.Height - 530, dim.Width - 600,dim.Height - 514 ),false, false, gui.Window, -1, false );
780 
781 	gui.ArchiveAdd = env->addButton ( rect<s32>( dim.Width - 725, dim.Height - 530, dim.Width - 665, dim.Height - 514 ), gui.Window,-1, L"add" );
782 	gui.ArchiveAdd->setToolTipText ( L"Add an archive, usually packed zip-archives (*.pk3) to the Filesystem" );
783 	gui.ArchiveRemove = env->addButton ( rect<s32>( dim.Width - 660, dim.Height - 530, dim.Width - 600, dim.Height - 514 ), gui.Window,-1, L"del" );
784 	gui.ArchiveRemove->setToolTipText ( L"Remove the selected archive from the FileSystem." );
785 	gui.ArchiveUp = env->addButton ( rect<s32>( dim.Width - 575, dim.Height - 530, dim.Width - 515, dim.Height - 514 ), gui.Window,-1, L"up" );
786 	gui.ArchiveUp->setToolTipText ( L"Arrange Archive Look-up Hirachy. Move the selected Archive up" );
787 	gui.ArchiveDown = env->addButton ( rect<s32>( dim.Width - 510, dim.Height - 530, dim.Width - 440, dim.Height - 514 ), gui.Window,-1, L"down" );
788 	gui.ArchiveDown->setToolTipText ( L"Arrange Archive Look-up Hirachy. Move the selected Archive down" );
789 
790 
791 	gui.ArchiveList = env->addTable ( rect<s32>( 5,dim.Height - 510, dim.Width - 450,dim.Height - 410 ), gui.Window  );
792 	gui.ArchiveList->addColumn ( L"Type", 0 );
793 	gui.ArchiveList->addColumn ( L"Real File Path", 1 );
794 	gui.ArchiveList->setColumnWidth ( 0, 60 );
795 	gui.ArchiveList->setColumnWidth ( 1, 284 );
796 	gui.ArchiveList->setToolTipText ( L"Show the attached Archives" );
797 
798 
799 	env->addStaticText ( L"Maps:", rect<s32>( 5, dim.Height - 400, dim.Width - 450,dim.Height - 380 ),false, false, gui.Window, -1, false );
800 	gui.MapList = env->addListBox ( rect<s32>( 5,dim.Height - 380, dim.Width - 450,dim.Height - 40  ), gui.Window, -1, true  );
801 	gui.MapList->setToolTipText ( L"Show the current Maps in all Archives.\n Double-Click the Map to start the level" );
802 
803 
804 	// create a visible Scene Tree
805 	env->addStaticText ( L"Scenegraph:", rect<s32>( dim.Width - 400, dim.Height - 400, dim.Width - 5,dim.Height - 380 ),false, false, gui.Window, -1, false );
806 	gui.SceneTree = env->addTreeView(	rect<s32>( dim.Width - 400, dim.Height - 380, dim.Width - 5, dim.Height - 40 ),
807 									gui.Window, -1, true, true, false );
808 	gui.SceneTree->setToolTipText ( L"Show the current Scenegraph" );
809 	gui.SceneTree->getRoot()->clearChildren();
810 	addSceneTreeItem ( Game->Device->getSceneManager()->getRootSceneNode(), gui.SceneTree->getRoot() );
811 
812 
813 	IGUIImageList* imageList = env->createImageList(	driver->getTexture ( "iconlist.png" ),
814 										dimension2di( 32, 32 ), true );
815 
816 	if ( imageList )
817 	{
818 		gui.SceneTree->setImageList( imageList );
819 		imageList->drop ();
820 	}
821 
822 
823 	// load the engine logo
824 	gui.Logo = env->addImage( driver->getTexture("irrlichtlogo3.png"), position2d<s32>(5, 16 ), true, 0 );
825 	gui.Logo->setToolTipText ( L"The great Irrlicht Engine" );
826 
827 	AddArchive ( "" );
828 }
829 
830 
831 /*
832 	Add an Archive to the FileSystems and updates the GUI
833 */
AddArchive(const path & archiveName)834 void CQuake3EventHandler::AddArchive ( const path& archiveName )
835 {
836 	IFileSystem *fs = Game->Device->getFileSystem();
837 	u32 i;
838 
839 	if ( archiveName.size () )
840 	{
841 		bool exists = false;
842 		for ( i = 0; i != fs->getFileArchiveCount(); ++i )
843 		{
844 			if ( fs->getFileArchive(i)->getFileList()->getPath() == archiveName )
845 			{
846 				exists = true;
847 				break;
848 			}
849 		}
850 
851 		if (!exists)
852 		{
853 			fs->addFileArchive(archiveName, true, false);
854 		}
855 	}
856 
857 	// store the current archives in game data
858 	// show the attached Archive in proper order
859 	if ( gui.ArchiveList )
860 	{
861 		gui.ArchiveList->clearRows();
862 
863 		for ( i = 0; i != fs->getFileArchiveCount(); ++i )
864 		{
865 			IFileArchive * archive = fs->getFileArchive ( i );
866 
867 			u32 index = gui.ArchiveList->addRow(i);
868 
869 			core::stringw typeName;
870 			switch(archive->getType())
871 			{
872 			case io::EFAT_ZIP:
873 				typeName = "ZIP";
874 				break;
875 			case io::EFAT_GZIP:
876 				typeName = "gzip";
877 				break;
878 			case io::EFAT_FOLDER:
879 				typeName = "Mount";
880 				break;
881 			case io::EFAT_PAK:
882 				typeName = "PAK";
883 				break;
884 			case io::EFAT_TAR:
885 				typeName = "TAR";
886 				break;
887 			default:
888 				typeName = "archive";
889 			}
890 
891 			gui.ArchiveList->setCellText ( index, 0, typeName );
892 			gui.ArchiveList->setCellText ( index, 1, archive->getFileList()->getPath() );
893 		}
894 	}
895 
896 
897 	// browse the archives for maps
898 	if ( gui.MapList )
899 	{
900 		gui.MapList->clear();
901 
902 		IGUISpriteBank *bank = Game->Device->getGUIEnvironment()->getSpriteBank("sprite_q3map");
903 		if ( 0 == bank )
904 			bank = Game->Device->getGUIEnvironment()->addEmptySpriteBank("sprite_q3map");
905 
906 		SGUISprite sprite;
907 		SGUISpriteFrame frame;
908 		core::rect<s32> r;
909 
910 		bank->getSprites().clear();
911 		bank->getPositions().clear ();
912 		gui.MapList->setSpriteBank ( bank );
913 
914 		u32 g = 0;
915 		core::stringw s;
916 
917 		// browse the attached file system
918 		fs->setFileListSystem ( FILESYSTEM_VIRTUAL );
919 		fs->changeWorkingDirectoryTo ( "/maps/" );
920 		IFileList *fileList = fs->createFileList ();
921 		fs->setFileListSystem ( FILESYSTEM_NATIVE );
922 
923 		for ( i=0; i< fileList->getFileCount(); ++i)
924 		{
925 			s = fileList->getFullFileName(i);
926 			if ( s.find ( ".bsp" ) >= 0 )
927 			{
928 				// get level screenshot. reformat texture to 128x128
929 				path c ( s );
930 				deletePathFromFilename ( c );
931 				cutFilenameExtension ( c, c );
932 				c = path ( "levelshots/" ) + c;
933 
934 				dimension2du dim ( 128, 128 );
935 				IVideoDriver * driver = Game->Device->getVideoDriver();
936 				IImage* image = 0;
937 				ITexture *tex = 0;
938 				path filename;
939 
940 				filename = c + ".jpg";
941 				if ( fs->existFile ( filename ) )
942 					image = driver->createImageFromFile( filename );
943 				if ( 0 == image )
944 				{
945 					filename = c + ".tga";
946 					if ( fs->existFile ( filename ) )
947 						image = driver->createImageFromFile( filename );
948 				}
949 
950 				if ( image )
951 				{
952 					IImage* filter = driver->createImage ( video::ECF_R8G8B8, dim );
953 					image->copyToScalingBoxFilter ( filter, 0 );
954 					image->drop ();
955 					image = filter;
956 				}
957 
958 				if ( image )
959 				{
960 					tex = driver->addTexture ( filename, image );
961 					image->drop ();
962 				}
963 
964 
965 				bank->setTexture ( g, tex );
966 
967 				r.LowerRightCorner.X = dim.Width;
968 				r.LowerRightCorner.Y = dim.Height;
969 				gui.MapList->setItemHeight ( r.LowerRightCorner.Y + 4 );
970 				frame.rectNumber = bank->getPositions().size();
971 				frame.textureNumber = g;
972 
973 				bank->getPositions().push_back(r);
974 
975 				sprite.Frames.set_used ( 0 );
976 				sprite.Frames.push_back(frame);
977 				sprite.frameTime = 0;
978 				bank->getSprites().push_back(sprite);
979 
980 				gui.MapList->addItem ( s.c_str (), g );
981 				g += 1;
982 			}
983 		}
984 		fileList->drop ();
985 
986 		gui.MapList->setSelected ( -1 );
987 		IGUIScrollBar * bar = (IGUIScrollBar*)gui.MapList->getElementFromId( 0 );
988 		if ( bar )
989 			bar->setPos ( 0 );
990 
991 	}
992 
993 }
994 
995 /*
996 	clears the Map in Memory
997 */
dropMap()998 void CQuake3EventHandler::dropMap ()
999 {
1000 	IVideoDriver * driver = Game->Device->getVideoDriver();
1001 
1002 	driver->removeAllHardwareBuffers ();
1003 	driver->removeAllTextures ();
1004 
1005 	Player[0].shutdown ();
1006 
1007 
1008 	dropElement ( ItemParent );
1009 	dropElement ( ShaderParent );
1010 	dropElement ( UnresolvedParent );
1011 	dropElement ( FogParent );
1012 	dropElement ( BulletParent );
1013 
1014 
1015 	Impacts.clear();
1016 
1017 	if ( Meta )
1018 	{
1019 		Meta = 0;
1020 	}
1021 
1022 	dropElement ( MapParent );
1023 	dropElement ( SkyNode );
1024 
1025 	// clean out meshes, because textures are invalid
1026 	// TODO: better texture handling;-)
1027 	IMeshCache *cache = Game->Device->getSceneManager ()->getMeshCache();
1028 	cache->clear ();
1029 	Mesh = 0;
1030 }
1031 
1032 /* Load new map
1033 */
LoadMap(const stringw & mapName,s32 collision)1034 void CQuake3EventHandler::LoadMap ( const stringw &mapName, s32 collision )
1035 {
1036 	if ( 0 == mapName.size() )
1037 		return;
1038 
1039 	dropMap ();
1040 
1041 	IFileSystem *fs = Game->Device->getFileSystem();
1042 	ISceneManager *smgr = Game->Device->getSceneManager ();
1043 
1044 	IReadFile* file = fs->createMemoryReadFile(&Game->loadParam,
1045 				sizeof(Game->loadParam), L"levelparameter.cfg", false);
1046 
1047 	// load cfg file
1048 	smgr->getMesh( file );
1049 	file->drop ();
1050 
1051 	// load the actual map
1052 	Mesh = (IQ3LevelMesh*) smgr->getMesh(mapName);
1053 	if ( 0 == Mesh )
1054 		return;
1055 
1056 	/*
1057 		add the geometry mesh to the Scene ( polygon & patches )
1058 		The Geometry mesh is optimised for faster drawing
1059 	*/
1060 
1061 	IMesh *geometry = Mesh->getMesh(E_Q3_MESH_GEOMETRY);
1062 	if ( 0 == geometry || geometry->getMeshBufferCount() == 0)
1063 		return;
1064 
1065 	Game->CurrentMapName = mapName;
1066 
1067 	//create a collision list
1068 	Meta = 0;
1069 
1070 	ITriangleSelector * selector = 0;
1071 	if (collision)
1072 		Meta = smgr->createMetaTriangleSelector();
1073 
1074 	//IMeshBuffer *b0 = geometry->getMeshBuffer(0);
1075 	//s32 minimalNodes = b0 ? core::s32_max ( 2048, b0->getVertexCount() / 32 ) : 2048;
1076 	s32 minimalNodes = 2048;
1077 
1078 	MapParent = smgr->addOctreeSceneNode(geometry, 0, -1, minimalNodes);
1079 	MapParent->setName ( mapName );
1080 	if ( Meta )
1081 	{
1082 		selector = smgr->createOctreeTriangleSelector( geometry,MapParent, minimalNodes);
1083 		//selector = smgr->createTriangleSelector ( geometry, MapParent );
1084 		Meta->addTriangleSelector( selector);
1085 		selector->drop ();
1086 	}
1087 
1088 	// logical parent for the items
1089 	ItemParent = smgr->addEmptySceneNode();
1090 	if ( ItemParent )
1091 		ItemParent->setName ( "Item Container" );
1092 
1093 	ShaderParent = smgr->addEmptySceneNode();
1094 	if ( ShaderParent )
1095 		ShaderParent->setName ( "Shader Container" );
1096 
1097 	UnresolvedParent = smgr->addEmptySceneNode();
1098 	if ( UnresolvedParent )
1099 		UnresolvedParent->setName ( "Unresolved Container" );
1100 
1101 	FogParent = smgr->addEmptySceneNode();
1102 	if ( FogParent )
1103 		FogParent->setName ( "Fog Container" );
1104 
1105 	// logical parent for the bullets
1106 	BulletParent = smgr->addEmptySceneNode();
1107 	if ( BulletParent )
1108 		BulletParent->setName ( "Bullet Container" );
1109 
1110 	/*
1111 		now construct SceneNodes for each Shader
1112 		The Objects are stored in the quake mesh E_Q3_MESH_ITEMS
1113 		and the Shader ID is stored in the MaterialParameters
1114 		mostly dark looking skulls and moving lava.. or green flashing tubes?
1115 	*/
1116 	Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_ITEMS,ShaderParent, Meta, false );
1117 	Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_FOG,FogParent, 0, false );
1118 	Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_UNRESOLVED,UnresolvedParent, Meta, true );
1119 
1120 	/*
1121 		Now construct Models from Entity List
1122 	*/
1123 	Q3ModelFactory ( Game->loadParam, Game->Device, Mesh, ItemParent, false );
1124 }
1125 
1126 /*
1127 	Adds a SceneNode with an icon to the Scene Tree
1128 */
addSceneTreeItem(ISceneNode * parent,IGUITreeViewNode * nodeParent)1129 void CQuake3EventHandler::addSceneTreeItem( ISceneNode * parent, IGUITreeViewNode* nodeParent)
1130 {
1131 	IGUITreeViewNode* node;
1132 	wchar_t msg[128];
1133 
1134 	s32 imageIndex;
1135 	list<ISceneNode*>::ConstIterator it = parent->getChildren().begin();
1136 	for (; it != parent->getChildren().end(); ++it)
1137 	{
1138 		switch ( (*it)->getType () )
1139 		{
1140 			case ESNT_Q3SHADER_SCENE_NODE: imageIndex = 0; break;
1141 			case ESNT_CAMERA: imageIndex = 1; break;
1142 			case ESNT_EMPTY: imageIndex = 2; break;
1143 			case ESNT_MESH: imageIndex = 3; break;
1144 			case ESNT_OCTREE: imageIndex = 3; break;
1145 			case ESNT_ANIMATED_MESH: imageIndex = 4; break;
1146 			case ESNT_SKY_BOX: imageIndex = 5; break;
1147 			case ESNT_BILLBOARD: imageIndex = 6; break;
1148 			case ESNT_PARTICLE_SYSTEM: imageIndex = 7; break;
1149 			case ESNT_TEXT: imageIndex = 8; break;
1150 			default:imageIndex = -1; break;
1151 		}
1152 
1153 		if ( imageIndex < 0 )
1154 		{
1155 			swprintf ( msg, 128, L"%hs,%hs",
1156 				Game->Device->getSceneManager ()->getSceneNodeTypeName ( (*it)->getType () ),
1157 				(*it)->getName()
1158 				);
1159 		}
1160 		else
1161 		{
1162 			swprintf ( msg, 128, L"%hs",(*it)->getName() );
1163 		}
1164 
1165 		node = nodeParent->addChildBack( msg, 0, imageIndex );
1166 
1167 		// Add all Animators
1168 		list<ISceneNodeAnimator*>::ConstIterator ait = (*it)->getAnimators().begin();
1169 		for (; ait != (*it)->getAnimators().end(); ++ait)
1170 		{
1171 			imageIndex = -1;
1172 			swprintf ( msg, 128, L"%hs",
1173 				Game->Device->getSceneManager ()->getAnimatorTypeName ( (*ait)->getType () )
1174 				);
1175 
1176 			switch ( (*ait)->getType () )
1177 			{
1178 				case ESNAT_FLY_CIRCLE:
1179 				case ESNAT_FLY_STRAIGHT:
1180 				case ESNAT_FOLLOW_SPLINE:
1181 				case ESNAT_ROTATION:
1182 				case ESNAT_TEXTURE:
1183 				case ESNAT_DELETION:
1184 				case ESNAT_COLLISION_RESPONSE:
1185 				case ESNAT_CAMERA_FPS:
1186 				case ESNAT_CAMERA_MAYA:
1187 				default:
1188 					break;
1189 			}
1190 			node->addChildBack( msg, 0, imageIndex );
1191 		}
1192 
1193 		addSceneTreeItem ( *it, node );
1194 	}
1195 }
1196 
1197 
1198 // Adds life!
CreatePlayers()1199 void CQuake3EventHandler::CreatePlayers()
1200 {
1201 	Player[0].create ( Game->Device, Mesh, MapParent, Meta );
1202 }
1203 
1204 
1205 // Adds a skydome to the scene
AddSky(u32 dome,const c8 * texture)1206 void CQuake3EventHandler::AddSky( u32 dome, const c8 *texture)
1207 {
1208 	ISceneManager *smgr = Game->Device->getSceneManager ();
1209 	IVideoDriver * driver = Game->Device->getVideoDriver();
1210 
1211 	bool oldMipMapState = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
1212 	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
1213 
1214 	if ( 0 == dome )
1215 	{
1216 		// irrlicht order
1217 		//static const c8*p[] = { "ft", "lf", "bk", "rt", "up", "dn" };
1218 		// quake3 order
1219 		static const c8*p[] = { "ft", "rt", "bk", "lf", "up", "dn" };
1220 
1221 		u32 i = 0;
1222 		snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] );
1223 		SkyNode = smgr->addSkyBoxSceneNode( driver->getTexture ( buf ), 0, 0, 0, 0, 0 );
1224 
1225 		if (SkyNode)
1226 		{
1227 			for ( i = 0; i < 6; ++i )
1228 			{
1229 				snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] );
1230 				SkyNode->getMaterial(i).setTexture ( 0, driver->getTexture ( buf ) );
1231 			}
1232 		}
1233 	}
1234 	else
1235 	if ( 1 == dome )
1236 	{
1237 		snprintf ( buf, 64, "%s.jpg", texture );
1238 		SkyNode = smgr->addSkyDomeSceneNode(
1239 				driver->getTexture( buf ), 32,32,
1240 				1.f, 1.f, 1000.f, 0, 11);
1241 	}
1242 	else
1243 	if ( 2 == dome )
1244 	{
1245 		snprintf ( buf, 64, "%s.jpg", texture );
1246 		SkyNode = smgr->addSkyDomeSceneNode(
1247 				driver->getTexture( buf ), 16,8,
1248 				0.95f, 2.f, 1000.f, 0, 11);
1249 	}
1250 
1251 	if (SkyNode)
1252 		SkyNode->setName("Skydome");
1253 	//SkyNode->getMaterial(0).ZBuffer = video::EMDF_DEPTH_LESS_EQUAL;
1254 
1255 	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState);
1256 }
1257 
1258 
1259 // enable GUI elements
SetGUIActive(s32 command)1260 void CQuake3EventHandler::SetGUIActive( s32 command)
1261 {
1262 	bool inputState = false;
1263 
1264 	ICameraSceneNode * camera = Game->Device->getSceneManager()->getActiveCamera ();
1265 
1266 	switch ( command )
1267 	{
1268 		case 0: Game->guiActive = 0; inputState = !Game->guiActive; break;
1269 		case 1: Game->guiActive = 1; inputState = !Game->guiActive;;break;
1270 		case 2: Game->guiActive ^= 1; inputState = !Game->guiActive;break;
1271 		case 3:
1272 			if ( camera )
1273 				inputState = !camera->isInputReceiverEnabled();
1274 			break;
1275 	}
1276 
1277 	if ( camera )
1278 	{
1279 		camera->setInputReceiverEnabled ( inputState );
1280 		Game->Device->getCursorControl()->setVisible( !inputState );
1281 	}
1282 
1283 	if ( gui.Window )
1284 	{
1285 		gui.Window->setVisible ( Game->guiActive != 0 );
1286 	}
1287 
1288 	if ( Game->guiActive &&
1289 			gui.SceneTree && Game->Device->getGUIEnvironment()->getFocus() != gui.SceneTree
1290 		)
1291 	{
1292 		gui.SceneTree->getRoot()->clearChildren();
1293 		addSceneTreeItem ( Game->Device->getSceneManager()->getRootSceneNode(), gui.SceneTree->getRoot() );
1294 	}
1295 
1296 	Game->Device->getGUIEnvironment()->setFocus ( Game->guiActive ? gui.Window: 0 );
1297 }
1298 
1299 
1300 /*
1301 	Handle game input
1302 */
OnEvent(const SEvent & eve)1303 bool CQuake3EventHandler::OnEvent(const SEvent& eve)
1304 {
1305 	if ( eve.EventType == EET_LOG_TEXT_EVENT )
1306 	{
1307 		return false;
1308 	}
1309 
1310 	if ( Game->guiActive && eve.EventType == EET_GUI_EVENT )
1311 	{
1312 		if ( eve.GUIEvent.Caller == gui.MapList && eve.GUIEvent.EventType == gui::EGET_LISTBOX_SELECTED_AGAIN )
1313 		{
1314 			s32 selected = gui.MapList->getSelected();
1315 			if ( selected >= 0 )
1316 			{
1317 				stringw loadMap = gui.MapList->getListItem ( selected );
1318 				if ( 0 == MapParent || loadMap != Game->CurrentMapName )
1319 				{
1320 					printf ( "Loading map %ls\n", loadMap.c_str() );
1321 					LoadMap ( loadMap , 1 );
1322 					if ( 0 == Game->loadParam.loadSkyShader )
1323 					{
1324 						AddSky ( 1, "skydome2" );
1325 					}
1326 					CreatePlayers ();
1327 					CreateGUI ();
1328 					SetGUIActive ( 0 );
1329 					return true;
1330 				}
1331 			}
1332 		}
1333 		else
1334 		if ( eve.GUIEvent.Caller == gui.ArchiveRemove && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
1335 		{
1336 			Game->Device->getFileSystem()->removeFileArchive( gui.ArchiveList->getSelected() );
1337 			Game->CurrentMapName = "";
1338 			AddArchive ( "" );
1339 		}
1340 		else
1341 		if ( eve.GUIEvent.Caller == gui.ArchiveAdd && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
1342 		{
1343 			if ( 0 == gui.ArchiveFileOpen )
1344 			{
1345 				Game->Device->getFileSystem()->setFileListSystem ( FILESYSTEM_NATIVE );
1346 				gui.ArchiveFileOpen = Game->Device->getGUIEnvironment()->addFileOpenDialog ( L"Add Game Archive" , false,gui.Window  );
1347 			}
1348 		}
1349 		else
1350 		if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_FILE_SELECTED )
1351 		{
1352 			AddArchive ( gui.ArchiveFileOpen->getFileName() );
1353 			gui.ArchiveFileOpen = 0;
1354 		}
1355 		else
1356 		if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_DIRECTORY_SELECTED )
1357 		{
1358 			AddArchive ( gui.ArchiveFileOpen->getDirectoryName() );
1359 		}
1360 		else
1361 		if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED )
1362 		{
1363 			gui.ArchiveFileOpen = 0;
1364 		}
1365 		else
1366 		if ( ( eve.GUIEvent.Caller == gui.ArchiveUp || eve.GUIEvent.Caller == gui.ArchiveDown ) &&
1367 			eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
1368 		{
1369 			s32 rel = eve.GUIEvent.Caller == gui.ArchiveUp ? -1 : 1;
1370 			if ( Game->Device->getFileSystem()->moveFileArchive ( gui.ArchiveList->getSelected (), rel ) )
1371 			{
1372 				s32 newIndex = core::s32_clamp ( gui.ArchiveList->getSelected() + rel, 0, gui.ArchiveList->getRowCount() - 1 );
1373 				AddArchive ( "" );
1374 				gui.ArchiveList->setSelected ( newIndex );
1375 				Game->CurrentMapName = "";
1376 			}
1377 		}
1378 		else
1379 		if ( eve.GUIEvent.Caller == gui.VideoDriver && eve.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
1380 		{
1381 			Game->deviceParam.DriverType = (E_DRIVER_TYPE) gui.VideoDriver->getItemData ( gui.VideoDriver->getSelected() );
1382 		}
1383 		else
1384 		if ( eve.GUIEvent.Caller == gui.VideoMode && eve.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
1385 		{
1386 			u32 val = gui.VideoMode->getItemData ( gui.VideoMode->getSelected() );
1387 			Game->deviceParam.WindowSize.Width = val >> 16;
1388 			Game->deviceParam.WindowSize.Height = val & 0xFFFF;
1389 		}
1390 		else
1391 		if ( eve.GUIEvent.Caller == gui.FullScreen && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
1392 		{
1393 			Game->deviceParam.Fullscreen = gui.FullScreen->isChecked();
1394 		}
1395 		else
1396 		if ( eve.GUIEvent.Caller == gui.Bit32 && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
1397 		{
1398 			Game->deviceParam.Bits = gui.Bit32->isChecked() ? 32 : 16;
1399 		}
1400 		else
1401 		if ( eve.GUIEvent.Caller == gui.MultiSample && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
1402 		{
1403 			Game->deviceParam.AntiAlias = gui.MultiSample->getPos();
1404 		}
1405 		else
1406 		if ( eve.GUIEvent.Caller == gui.Tesselation && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
1407 		{
1408 			Game->loadParam.patchTesselation = gui.Tesselation->getPos ();
1409 		}
1410 		else
1411 		if ( eve.GUIEvent.Caller == gui.Gamma && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
1412 		{
1413 			Game->GammaValue = gui.Gamma->getPos () * 0.01f;
1414 			Game->Device->setGammaRamp ( Game->GammaValue, Game->GammaValue, Game->GammaValue, 0.f, 0.f );
1415 		}
1416 		else
1417 		if ( eve.GUIEvent.Caller == gui.SetVideoMode && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
1418 		{
1419 			Game->retVal = 2;
1420 			Game->Device->closeDevice();
1421 		}
1422 		else
1423 		if ( eve.GUIEvent.Caller == gui.Window && eve.GUIEvent.EventType == gui::EGET_ELEMENT_CLOSED )
1424 		{
1425 			Game->Device->closeDevice();
1426 		}
1427 		else
1428 		if ( eve.GUIEvent.Caller == gui.Collision && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
1429 		{
1430 			// set fly through active
1431 			Game->flyTroughState ^= 1;
1432 			Player[0].cam()->setAnimateTarget ( Game->flyTroughState == 0 );
1433 
1434 			printf ( "collision %d\n", Game->flyTroughState == 0 );
1435 		}
1436 		else
1437 		if ( eve.GUIEvent.Caller == gui.Visible_Map && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
1438 		{
1439 			bool v = gui.Visible_Map->isChecked();
1440 
1441 			if ( MapParent )
1442 			{
1443 				printf ( "static node set visible %d\n",v );
1444 				MapParent->setVisible ( v );
1445 			}
1446 		}
1447 		else
1448 		if ( eve.GUIEvent.Caller == gui.Visible_Shader && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
1449 		{
1450 			bool v = gui.Visible_Shader->isChecked();
1451 
1452 			if ( ShaderParent )
1453 			{
1454 				printf ( "shader node set visible %d\n",v );
1455 				ShaderParent->setVisible ( v );
1456 			}
1457 		}
1458 		else
1459 		if ( eve.GUIEvent.Caller == gui.Visible_Skydome && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
1460 		{
1461 			if ( SkyNode )
1462 			{
1463 				bool v = !SkyNode->isVisible();
1464 				printf ( "skynode set visible %d\n",v );
1465 				SkyNode->setVisible ( v );
1466 			}
1467 		}
1468 		else
1469 		if ( eve.GUIEvent.Caller == gui.Respawn && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
1470 		{
1471 			Player[0].respawn ();
1472 		}
1473 
1474 		return false;
1475 	}
1476 
1477 	// fire
1478 	if ((eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_SPACE &&
1479 		eve.KeyInput.PressedDown == false) ||
1480 		(eve.EventType == EET_MOUSE_INPUT_EVENT && eve.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
1481 		)
1482 	{
1483 		ICameraSceneNode * camera = Game->Device->getSceneManager()->getActiveCamera ();
1484 		if ( camera && camera->isInputReceiverEnabled () )
1485 		{
1486 			useItem( Player + 0 );
1487 		}
1488 	}
1489 
1490 	// gui active
1491 	if ((eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_F1 &&
1492 		eve.KeyInput.PressedDown == false) ||
1493 		(eve.EventType == EET_MOUSE_INPUT_EVENT && eve.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
1494 		)
1495 	{
1496 		SetGUIActive ( 2 );
1497 	}
1498 
1499 	// check if user presses the key
1500 	if ( eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.PressedDown == false)
1501 	{
1502 		// Escape toggles camera Input
1503 		if ( eve.KeyInput.Key == irr::KEY_ESCAPE )
1504 		{
1505 			SetGUIActive ( 3 );
1506 		}
1507 		else
1508 		if (eve.KeyInput.Key == KEY_F11)
1509 		{
1510 			// screenshot are taken without gamma!
1511 			IImage* image = Game->Device->getVideoDriver()->createScreenShot();
1512 			if (image)
1513 			{
1514 				core::vector3df pos;
1515 				core::vector3df rot;
1516 				ICameraSceneNode * cam = Game->Device->getSceneManager()->getActiveCamera ();
1517 				if ( cam )
1518 				{
1519 					pos = cam->getPosition ();
1520 					rot = cam->getRotation ();
1521 				}
1522 
1523 				static const c8 *dName[] = { "null", "software", "burning",
1524 					"d3d8", "d3d9", "opengl" };
1525 
1526 				snprintf(buf, 256, "%s_%ls_%.0f_%.0f_%.0f_%.0f_%.0f_%.0f.jpg",
1527 						dName[Game->Device->getVideoDriver()->getDriverType()],
1528 						Game->CurrentMapName.c_str(),
1529 						pos.X, pos.Y, pos.Z,
1530 						rot.X, rot.Y, rot.Z
1531 						);
1532 				path filename ( buf );
1533 				filename.replace ( '/', '_' );
1534 				printf ( "screenshot : %s\n", filename.c_str() );
1535 				Game->Device->getVideoDriver()->writeImageToFile(image, filename, 100 );
1536 				image->drop();
1537 			}
1538 		}
1539 		else
1540 		if (eve.KeyInput.Key == KEY_F9)
1541 		{
1542 			s32 value = EDS_OFF;
1543 
1544 			Game->debugState = ( Game->debugState + 1 ) & 3;
1545 
1546 			switch ( Game->debugState )
1547 			{
1548 				case 1: value = EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_BBOX_ALL; break;
1549 				case 2: value = EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_SKELETON; break;
1550 			}
1551 /*
1552 			// set debug map data on/off
1553 			debugState = debugState == EDS_OFF ?
1554 				EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_BBOX_ALL:
1555 				EDS_OFF;
1556 */
1557 			if ( ItemParent )
1558 			{
1559 				list<ISceneNode*>::ConstIterator it = ItemParent->getChildren().begin();
1560 				for (; it != ItemParent->getChildren().end(); ++it)
1561 				{
1562 					(*it)->setDebugDataVisible ( value );
1563 				}
1564 			}
1565 
1566 			if ( ShaderParent )
1567 			{
1568 				list<ISceneNode*>::ConstIterator it = ShaderParent->getChildren().begin();
1569 				for (; it != ShaderParent->getChildren().end(); ++it)
1570 				{
1571 					(*it)->setDebugDataVisible ( value );
1572 				}
1573 			}
1574 
1575 			if ( UnresolvedParent )
1576 			{
1577 				list<ISceneNode*>::ConstIterator it = UnresolvedParent->getChildren().begin();
1578 				for (; it != UnresolvedParent->getChildren().end(); ++it)
1579 				{
1580 					(*it)->setDebugDataVisible ( value );
1581 				}
1582 			}
1583 
1584 			if ( FogParent )
1585 			{
1586 				list<ISceneNode*>::ConstIterator it = FogParent->getChildren().begin();
1587 				for (; it != FogParent->getChildren().end(); ++it)
1588 				{
1589 					(*it)->setDebugDataVisible ( value );
1590 				}
1591 			}
1592 
1593 			if ( SkyNode )
1594 			{
1595 				SkyNode->setDebugDataVisible ( value );
1596 			}
1597 
1598 		}
1599 		else
1600 		if (eve.KeyInput.Key == KEY_F8)
1601 		{
1602 			// set gravity on/off
1603 			Game->gravityState ^= 1;
1604 			Player[0].cam()->setGravity ( getGravity ( Game->gravityState ? "earth" : "none" ) );
1605 			printf ( "gravity %s\n", Game->gravityState ? "earth" : "none" );
1606 		}
1607 		else
1608 		if (eve.KeyInput.Key == KEY_F7)
1609 		{
1610 			// set fly through active
1611 			Game->flyTroughState ^= 1;
1612 			Player[0].cam()->setAnimateTarget ( Game->flyTroughState == 0 );
1613 			if ( gui.Collision )
1614 				gui.Collision->setChecked ( Game->flyTroughState == 0 );
1615 
1616 			printf ( "collision %d\n", Game->flyTroughState == 0 );
1617 		}
1618 		else
1619 		if (eve.KeyInput.Key == KEY_F2)
1620 		{
1621 			Player[0].respawn ();
1622 		}
1623 		else
1624 		if (eve.KeyInput.Key == KEY_F3)
1625 		{
1626 			if ( MapParent )
1627 			{
1628 				bool v = !MapParent->isVisible ();
1629 				printf ( "static node set visible %d\n",v );
1630 				MapParent->setVisible ( v );
1631 				if ( gui.Visible_Map )
1632 					gui.Visible_Map->setChecked ( v );
1633 			}
1634 		}
1635 		else
1636 		if (eve.KeyInput.Key == KEY_F4)
1637 		{
1638 			if ( ShaderParent )
1639 			{
1640 				bool v = !ShaderParent->isVisible ();
1641 				printf ( "shader node set visible %d\n",v );
1642 				ShaderParent->setVisible ( v );
1643 				if ( gui.Visible_Shader )
1644 					gui.Visible_Shader->setChecked ( v );
1645 			}
1646 		}
1647 		else
1648 		if (eve.KeyInput.Key == KEY_F5)
1649 		{
1650 			if ( FogParent )
1651 			{
1652 				bool v = !FogParent->isVisible ();
1653 				printf ( "fog node set visible %d\n",v );
1654 				FogParent->setVisible ( v );
1655 				if ( gui.Visible_Fog )
1656 					gui.Visible_Fog->setChecked ( v );
1657 			}
1658 
1659 		}
1660 		else
1661 		if (eve.KeyInput.Key == KEY_F6)
1662 		{
1663 			if ( UnresolvedParent )
1664 			{
1665 				bool v = !UnresolvedParent->isVisible ();
1666 				printf ( "unresolved node set visible %d\n",v );
1667 				UnresolvedParent->setVisible ( v );
1668 				if ( gui.Visible_Unresolved )
1669 					gui.Visible_Unresolved->setChecked ( v );
1670 			}
1671 		}
1672 	}
1673 
1674 	// check if user presses the key C ( for crouch)
1675 	if ( eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_KEY_C )
1676 	{
1677 		// crouch
1678 		ISceneNodeAnimatorCollisionResponse *anim = Player[0].cam ();
1679 		if ( anim && 0 == Game->flyTroughState )
1680 		{
1681 			if ( false == eve.KeyInput.PressedDown )
1682 			{
1683 				// stand up
1684 				anim->setEllipsoidRadius (  vector3df(30,45,30) );
1685 				anim->setEllipsoidTranslation ( vector3df(0,40,0));
1686 
1687 			}
1688 			else
1689 			{
1690 				// on your knees
1691 				anim->setEllipsoidRadius (  vector3df(30,20,30) );
1692 				anim->setEllipsoidTranslation ( vector3df(0,20,0));
1693 			}
1694 			return true;
1695 		}
1696 	}
1697 	return false;
1698 }
1699 
1700 
1701 
1702 /*
1703 	useItem
1704 */
useItem(Q3Player * player)1705 void CQuake3EventHandler::useItem( Q3Player * player)
1706 {
1707 	ISceneManager* smgr = Game->Device->getSceneManager();
1708 	ICameraSceneNode* camera = smgr->getActiveCamera();
1709 
1710 	if (!camera)
1711 		return;
1712 
1713 	SParticleImpact imp;
1714 	imp.when = 0;
1715 
1716 	// get line of camera
1717 
1718 	vector3df start = camera->getPosition();
1719 
1720 	if ( player->WeaponNode )
1721 	{
1722 		start.X += 0.f;
1723 		start.Y += 0.f;
1724 		start.Z += 0.f;
1725 	}
1726 
1727 	vector3df end = (camera->getTarget() - start);
1728 	end.normalize();
1729 	start += end*20.0f;
1730 
1731 	end = start + (end * camera->getFarValue());
1732 
1733 	triangle3df triangle;
1734 	line3d<f32> line(start, end);
1735 
1736 	// get intersection point with map
1737 	scene::ISceneNode* hitNode;
1738 	if (smgr->getSceneCollisionManager()->getCollisionPoint(
1739 		line, Meta, end, triangle,hitNode))
1740 	{
1741 		// collides with wall
1742 		vector3df out = triangle.getNormal();
1743 		out.setLength(0.03f);
1744 
1745 		imp.when = 1;
1746 		imp.outVector = out;
1747 		imp.pos = end;
1748 
1749 		player->setAnim ( "pow" );
1750 		player->Anim[1].next += player->Anim[1].delta;
1751 	}
1752 	else
1753 	{
1754 		// doesnt collide with wall
1755 		vector3df start = camera->getPosition();
1756 		if ( player->WeaponNode )
1757 		{
1758 			//start.X += 10.f;
1759 			//start.Y += -5.f;
1760 			//start.Z += 1.f;
1761 		}
1762 
1763 		vector3df end = (camera->getTarget() - start);
1764 		end.normalize();
1765 		start += end*20.0f;
1766 		end = start + (end * camera->getFarValue());
1767 	}
1768 
1769 	// create fire ball
1770 	ISceneNode* node = 0;
1771 	node = smgr->addBillboardSceneNode( BulletParent,dimension2d<f32>(10,10), start);
1772 
1773 	node->setMaterialFlag(EMF_LIGHTING, false);
1774 	node->setMaterialTexture(0, Game->Device->getVideoDriver()->getTexture("fireball.bmp"));
1775 	node->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
1776 	node->setMaterialType(EMT_TRANSPARENT_ADD_COLOR);
1777 
1778 	f32 length = (f32)(end - start).getLength();
1779 	const f32 speed = 5.8f;
1780 	u32 time = (u32)(length / speed);
1781 
1782 	ISceneNodeAnimator* anim = 0;
1783 
1784 	// set flight line
1785 
1786 	anim = smgr->createFlyStraightAnimator(start, end, time);
1787 	node->addAnimator(anim);
1788 	anim->drop();
1789 
1790 	snprintf ( buf, 64, "bullet: %s on %.1f,%1.f,%1.f",
1791 				imp.when ? "hit" : "nohit", end.X, end.Y, end.Z );
1792 	node->setName ( buf );
1793 
1794 
1795 	anim = smgr->createDeleteAnimator(time);
1796 	node->addAnimator(anim);
1797 	anim->drop();
1798 
1799 	if (imp.when)
1800 	{
1801 		// create impact note
1802 		imp.when = Game->Device->getTimer()->getTime() +
1803 			(time + (s32) ( ( 1.f + Noiser::get() ) * 250.f ));
1804 		Impacts.push_back(imp);
1805 	}
1806 
1807 	// play sound
1808 }
1809 
1810 // rendered when bullets hit something
createParticleImpacts(u32 now)1811 void CQuake3EventHandler::createParticleImpacts( u32 now )
1812 {
1813 	ISceneManager* sm = Game->Device->getSceneManager();
1814 
1815 	struct smokeLayer
1816 	{
1817 		const c8 * texture;
1818 		f32 scale;
1819 		f32 minparticleSize;
1820 		f32 maxparticleSize;
1821 		f32 boxSize;
1822 		u32 minParticle;
1823 		u32 maxParticle;
1824 		u32 fadeout;
1825 		u32 lifetime;
1826 	};
1827 
1828 	smokeLayer smoke[] =
1829 	{
1830 		{ "smoke2.jpg", 0.4f, 1.5f, 18.f, 20.f, 20, 50, 2000, 10000 },
1831 		{ "smoke3.jpg", 0.2f, 1.2f, 15.f, 20.f, 10, 30, 1000, 12000 }
1832 	};
1833 
1834 
1835 	u32 i;
1836 	u32 g;
1837 	s32 factor = 1;
1838 	for ( g = 0; g != 2; ++g )
1839 	{
1840 		smoke[g].minParticle *= factor;
1841 		smoke[g].maxParticle *= factor;
1842 		smoke[g].lifetime *= factor;
1843 		smoke[g].boxSize *= Noiser::get() * 0.5f;
1844 	}
1845 
1846 	for ( i=0; i < Impacts.size(); ++i)
1847 	{
1848 		if (now < Impacts[i].when)
1849 			continue;
1850 
1851 		// create smoke particle system
1852 		IParticleSystemSceneNode* pas = 0;
1853 
1854 		for ( g = 0; g != 2; ++g )
1855 		{
1856 			pas = sm->addParticleSystemSceneNode(false, BulletParent, -1, Impacts[i].pos);
1857 
1858 			snprintf ( buf, 64, "bullet impact smoke at %.1f,%.1f,%1.f",
1859 				Impacts[i].pos.X,Impacts[i].pos.Y,Impacts[i].pos.Z);
1860 			pas->setName ( buf );
1861 
1862 			// create a flat smoke
1863 			vector3df direction = Impacts[i].outVector;
1864 			direction *= smoke[g].scale;
1865 			IParticleEmitter* em = pas->createBoxEmitter(
1866 				aabbox3d<f32>(-4.f,0.f,-4.f,20.f,smoke[g].minparticleSize,20.f),
1867 				direction,smoke[g].minParticle, smoke[g].maxParticle,
1868 				video::SColor(0,0,0,0),video::SColor(0,128,128,128),
1869 				250,4000, 60);
1870 
1871 			em->setMinStartSize (dimension2d<f32>( smoke[g].minparticleSize, smoke[g].minparticleSize));
1872 			em->setMaxStartSize (dimension2d<f32>( smoke[g].maxparticleSize, smoke[g].maxparticleSize));
1873 
1874 			pas->setEmitter(em);
1875 			em->drop();
1876 
1877 			// particles get invisible
1878 			IParticleAffector* paf = pas->createFadeOutParticleAffector(
1879 				video::SColor ( 0, 0, 0, 0 ), smoke[g].fadeout);
1880 			pas->addAffector(paf);
1881 			paf->drop();
1882 
1883 			// particle system life time
1884 			ISceneNodeAnimator* anim = sm->createDeleteAnimator( smoke[g].lifetime);
1885 			pas->addAnimator(anim);
1886 			anim->drop();
1887 
1888 			pas->setMaterialFlag(video::EMF_LIGHTING, false);
1889 			pas->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
1890 			pas->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
1891 			pas->setMaterialTexture(0, Game->Device->getVideoDriver()->getTexture( smoke[g].texture ));
1892 		}
1893 
1894 
1895 		// play impact sound
1896 		#ifdef USE_IRRKLANG
1897 /*
1898 		if (irrKlang)
1899 		{
1900 			audio::ISound* sound =
1901 				irrKlang->play3D(impactSound, Impacts[i].pos, false, false, true);
1902 
1903 			if (sound)
1904 			{
1905 				// adjust max value a bit to make to sound of an impact louder
1906 				sound->setMinDistance(400);
1907 				sound->drop();
1908 			}
1909 		}
1910 */
1911 		#endif
1912 
1913 
1914 		// delete entry
1915 		Impacts.erase(i);
1916 		i--;
1917 	}
1918 }
1919 
1920 /*
1921 	render
1922 */
Render()1923 void CQuake3EventHandler::Render()
1924 {
1925 	IVideoDriver * driver = Game->Device->getVideoDriver();
1926 	if ( 0 == driver )
1927 		return;
1928 
1929 	// TODO: This does not work, yet.
1930 	const bool anaglyph=false;
1931 	if (anaglyph)
1932 	{
1933 		scene::ICameraSceneNode* cameraOld = Game->Device->getSceneManager()->getActiveCamera();
1934 		driver->beginScene(true, true, SColor(0,0,0,0));
1935 		driver->getOverrideMaterial().Material.ColorMask = ECP_NONE;
1936 		driver->getOverrideMaterial().EnableFlags  = EMF_COLOR_MASK;
1937         driver->getOverrideMaterial().EnablePasses = ESNRP_SKY_BOX +
1938                                                      ESNRP_SOLID +
1939                                                      ESNRP_TRANSPARENT +
1940                                                      ESNRP_TRANSPARENT_EFFECT +
1941                                                      ESNRP_SHADOW;
1942 		Game->Device->getSceneManager()->drawAll();
1943 		driver->clearZBuffer();
1944 
1945 		const vector3df oldPosition = cameraOld->getPosition();
1946 		const vector3df oldTarget   = cameraOld->getTarget();
1947 		const matrix4 startMatrix   = cameraOld->getAbsoluteTransformation();
1948 		const vector3df focusPoint  = (oldTarget -
1949 				cameraOld->getAbsolutePosition()).setLength(10000) +
1950 				cameraOld->getAbsolutePosition() ;
1951 
1952 		scene::ICameraSceneNode* camera = cameraOld;//Game->Device->getSceneManager()->addCameraSceneNode();
1953 
1954 		//Left eye...
1955 		vector3df pos;
1956 		matrix4   move;
1957 
1958 		move.setTranslation( vector3df(-1.5f,0.0f,0.0f) );
1959 		pos=(startMatrix*move).getTranslation();
1960 
1961 		driver->getOverrideMaterial().Material.ColorMask = ECP_RED;
1962 		driver->getOverrideMaterial().EnableFlags  = EMF_COLOR_MASK;
1963 		driver->getOverrideMaterial().EnablePasses =
1964 				ESNRP_SKY_BOX|ESNRP_SOLID|ESNRP_TRANSPARENT|
1965 				ESNRP_TRANSPARENT_EFFECT|ESNRP_SHADOW;
1966 
1967 		camera->setPosition(pos);
1968 		camera->setTarget(focusPoint);
1969 
1970 		Game->Device->getSceneManager()->drawAll();
1971 		driver->clearZBuffer();
1972 
1973 		//Right eye...
1974 		move.setTranslation( vector3df(1.5f,0.0f,0.0f) );
1975 		pos=(startMatrix*move).getTranslation();
1976 
1977 		driver->getOverrideMaterial().Material.ColorMask = ECP_GREEN + ECP_BLUE;
1978 		driver->getOverrideMaterial().EnableFlags  = EMF_COLOR_MASK;
1979 		driver->getOverrideMaterial().EnablePasses =
1980 				ESNRP_SKY_BOX|ESNRP_SOLID|ESNRP_TRANSPARENT|
1981 				ESNRP_TRANSPARENT_EFFECT|ESNRP_SHADOW;
1982 
1983 		camera->setPosition(pos);
1984 		camera->setTarget(focusPoint);
1985 
1986 		Game->Device->getSceneManager()->drawAll();
1987 
1988 		driver->getOverrideMaterial().Material.ColorMask=ECP_ALL;
1989 		driver->getOverrideMaterial().EnableFlags=0;
1990 		driver->getOverrideMaterial().EnablePasses=0;
1991 
1992 		if (camera != cameraOld)
1993 		{
1994 			Game->Device->getSceneManager()->setActiveCamera(cameraOld);
1995 			camera->remove();
1996 		}
1997 		else
1998 		{
1999 			camera->setPosition(oldPosition);
2000 			camera->setTarget(oldTarget);
2001 		}
2002 	}
2003 	else
2004 	{
2005 		driver->beginScene(true, true, SColor(0,0,0,0));
2006 		Game->Device->getSceneManager()->drawAll();
2007 	}
2008 	Game->Device->getGUIEnvironment()->drawAll();
2009 	driver->endScene();
2010 }
2011 
2012 /*
2013 	update the generic scene node
2014 */
Animate()2015 void CQuake3EventHandler::Animate()
2016 {
2017 	u32 now = Game->Device->getTimer()->getTime();
2018 
2019 	Q3Player * player = Player + 0;
2020 
2021 	checkTimeFire ( player->Anim, 4, now );
2022 
2023 	// Query Scene Manager attributes
2024 	if ( player->Anim[0].flags & FIRED )
2025 	{
2026 		ISceneManager *smgr = Game->Device->getSceneManager ();
2027 		wchar_t msg[128];
2028 		IVideoDriver * driver = Game->Device->getVideoDriver();
2029 
2030 		IAttributes * attr = smgr->getParameters();
2031 #ifdef _IRR_SCENEMANAGER_DEBUG
2032 		swprintf ( msg, 128,
2033 			L"Q3 %s [%ls], FPS:%03d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)",
2034 			Game->CurrentMapName.c_str(),
2035 			driver->getName(),
2036 			driver->getFPS (),
2037 			(f32) driver->getPrimitiveCountDrawn( 0 ) * ( 1.f / 1000000.f ),
2038 			attr->getAttributeAsInt ( "culled" ),
2039 			attr->getAttributeAsInt ( "calls" ),
2040 			attr->getAttributeAsInt ( "drawn_solid" ),
2041 			attr->getAttributeAsInt ( "drawn_transparent" ),
2042 			attr->getAttributeAsInt ( "drawn_transparent_effect" )
2043 			);
2044 #else
2045 swprintf ( msg, 128,
2046 			L"Q3 %s [%ls], FPS:%03d Tri:%.03fm",
2047 			Game->CurrentMapName.c_str(),
2048 			driver->getName(),
2049 			driver->getFPS (),
2050 			(f32) driver->getPrimitiveCountDrawn( 0 ) * ( 1.f / 1000000.f )
2051 			);
2052 #endif
2053 		Game->Device->setWindowCaption( msg );
2054 
2055 		swprintf ( msg, 128,
2056 					L"%03d fps, F1 GUI on/off, F2 respawn, F3-F6 toggle Nodes, F7 Collision on/off"
2057 					L", F8 Gravity on/off, Right Mouse Toggle GUI",
2058 					Game->Device->getVideoDriver()->getFPS ()
2059 				);
2060 		if ( gui.StatusLine )
2061 			gui.StatusLine->setText ( msg );
2062 		player->Anim[0].flags &= ~FIRED;
2063 	}
2064 
2065 	// idle..
2066 	if ( player->Anim[1].flags & FIRED )
2067 	{
2068 		if ( strcmp ( player->animation, "idle" ) )
2069 			player->setAnim ( "idle" );
2070 
2071 		player->Anim[1].flags &= ~FIRED;
2072 	}
2073 
2074 	createParticleImpacts ( now );
2075 
2076 }
2077 
2078 
2079 /* The main game states
2080 */
runGame(GameData * game)2081 void runGame ( GameData *game )
2082 {
2083 	if ( game->retVal >= 3 )
2084 		return;
2085 
2086 	game->Device = (*game->createExDevice) ( game->deviceParam );
2087 	if ( 0 == game->Device)
2088 	{
2089 		// could not create selected driver.
2090 		game->retVal = 0;
2091 		return;
2092 	}
2093 
2094 	// create an event receiver based on current game data
2095 	CQuake3EventHandler *eventHandler = new CQuake3EventHandler( game );
2096 
2097 	// load stored config
2098 	game->load ( "explorer.cfg" );
2099 
2100 	// add our media directory and archive to the file system
2101 	for ( u32 i = 0; i < game->CurrentArchiveList.size(); ++i )
2102 	{
2103 		eventHandler->AddArchive ( game->CurrentArchiveList[i] );
2104 	}
2105 
2106 	// Load a Map or startup to the GUI
2107 	if ( game->CurrentMapName.size () )
2108 	{
2109 		eventHandler->LoadMap ( game->CurrentMapName, 1 );
2110 		if ( 0 == game->loadParam.loadSkyShader )
2111 			eventHandler->AddSky ( 1, "skydome2" );
2112 		eventHandler->CreatePlayers ();
2113 		eventHandler->CreateGUI ();
2114 		eventHandler->SetGUIActive ( 0 );
2115 
2116 		// set player to last position on restart
2117 		if ( game->retVal == 2 )
2118 		{
2119 			eventHandler->GetPlayer( 0 )->setpos ( game->PlayerPosition, game->PlayerRotation );
2120 		}
2121 	}
2122 	else
2123 	{
2124 		// start up empty
2125 		eventHandler->AddSky ( 1, "skydome2" );
2126 		eventHandler->CreatePlayers ();
2127 		eventHandler->CreateGUI ();
2128 		eventHandler->SetGUIActive ( 1 );
2129 		background_music ( "IrrlichtTheme.ogg" );
2130 	}
2131 
2132 
2133 	game->retVal = 3;
2134 	while( game->Device->run() )
2135 	{
2136 		eventHandler->Animate ();
2137 		eventHandler->Render ();
2138 		//if ( !game->Device->isWindowActive() )
2139 			game->Device->yield();
2140 	}
2141 
2142 	game->Device->setGammaRamp ( 1.f, 1.f, 1.f, 0.f, 0.f );
2143 	delete eventHandler;
2144 }
2145 
2146 #if defined (_IRR_WINDOWS_) && 0
2147 	#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
2148 #endif
2149 
2150 
2151 /* The main routine, doing all setup
2152 */
main(int argc,char * argv[])2153 int IRRCALLCONV main(int argc, char* argv[])
2154 {
2155 	path prgname(argv[0]);
2156 	GameData game ( deletePathFromPath ( prgname, 1 ) );
2157 
2158 	// dynamically load irrlicht
2159 	const c8 * dllName = argc > 1 ? argv[1] : "irrlicht.dll";
2160 	game.createExDevice = load_createDeviceEx ( dllName );
2161 	if ( 0 == game.createExDevice )
2162 	{
2163 		game.retVal = 3;
2164 		printf ( "Could not load %s.\n", dllName );
2165 		return game.retVal; // could not load dll
2166 	}
2167 
2168 	// start without asking for driver
2169 	game.retVal = 1;
2170 	do
2171 	{
2172 		// if driver could not created, ask for another driver
2173 		if ( game.retVal == 0 )
2174 		{
2175 			game.setDefault ();
2176 			// ask user for driver
2177 			game.deviceParam.DriverType=driverChoiceConsole();
2178 			if (game.deviceParam.DriverType==video::EDT_COUNT)
2179 				game.retVal = 3;
2180 		}
2181 		runGame ( &game );
2182 	} while ( game.retVal < 3 );
2183 
2184 	return game.retVal;
2185 }
2186 
2187 /*
2188 **/
2189