1 /*!
2 Model Factory.
3 create the additional scenenodes for ( bullets, health... )
4
5 Defines the Entities for Quake3
6 */
7
8 #include <irrlicht.h>
9 #include "q3factory.h"
10 #include "sound.h"
11
12 using namespace irr;
13 using namespace scene;
14 using namespace gui;
15 using namespace video;
16 using namespace core;
17 using namespace quake3;
18
19 //! This list is based on the original quake3.
20 static const SItemElement Quake3ItemElement [] = {
21 { "item_health",
22 {"models/powerups/health/medium_cross.md3",
23 "models/powerups/health/medium_sphere.md3"},
24 "sound/items/n_health.wav",
25 "icons/iconh_yellow",
26 "25 Health",
27 25,
28 HEALTH,
29 SUB_NONE,
30 SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
31 },
32 { "item_health_large",
33 "models/powerups/health/large_cross.md3",
34 "models/powerups/health/large_sphere.md3",
35 "sound/items/l_health.wav",
36 "icons/iconh_red",
37 "50 Health",
38 50,
39 HEALTH,
40 SUB_NONE,
41 SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
42 },
43 {
44 "item_health_mega",
45 "models/powerups/health/mega_cross.md3",
46 "models/powerups/health/mega_sphere.md3",
47 "sound/items/m_health.wav",
48 "icons/iconh_mega",
49 "Mega Health",
50 100,
51 HEALTH,
52 SUB_NONE,
53 SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
54 },
55 {
56 "item_health_small",
57 "models/powerups/health/small_cross.md3",
58 "models/powerups/health/small_sphere.md3",
59 "sound/items/s_health.wav",
60 "icons/iconh_green",
61 "5 Health",
62 5,
63 HEALTH,
64 SUB_NONE,
65 SPECIAL_SFX_BOUNCE | SPECIAL_SFX_ROTATE_1
66 },
67 { "ammo_bullets",
68 "models/powerups/ammo/machinegunam.md3",
69 "",
70 "sound/misc/am_pkup.wav",
71 "icons/icona_machinegun",
72 "Bullets",
73 50,
74 AMMO,
75 MACHINEGUN,
76 SPECIAL_SFX_BOUNCE,
77 },
78 {
79 "ammo_cells",
80 "models/powerups/ammo/plasmaam.md3",
81 "",
82 "sound/misc/am_pkup.wav",
83 "icons/icona_plasma",
84 "Cells",
85 30,
86 AMMO,
87 PLASMAGUN,
88 SPECIAL_SFX_BOUNCE
89 },
90 { "ammo_rockets",
91 "models/powerups/ammo/rocketam.md3",
92 "",
93 "",
94 "icons/icona_rocket",
95 "Rockets",
96 5,
97 AMMO,
98 ROCKET_LAUNCHER,
99 SPECIAL_SFX_ROTATE
100 },
101 {
102 "ammo_shells",
103 "models/powerups/ammo/shotgunam.md3",
104 "",
105 "sound/misc/am_pkup.wav",
106 "icons/icona_shotgun",
107 "Shells",
108 10,
109 AMMO,
110 SHOTGUN,
111 SPECIAL_SFX_ROTATE
112 },
113 {
114 "ammo_slugs",
115 "models/powerups/ammo/railgunam.md3",
116 "",
117 "sound/misc/am_pkup.wav",
118 "icons/icona_railgun",
119 "Slugs",
120 10,
121 AMMO,
122 RAILGUN,
123 SPECIAL_SFX_ROTATE
124 },
125 {
126 "item_armor_body",
127 "models/powerups/armor/armor_red.md3",
128 "",
129 "sound/misc/ar2_pkup.wav",
130 "icons/iconr_red",
131 "Heavy Armor",
132 100,
133 ARMOR,
134 SUB_NONE,
135 SPECIAL_SFX_ROTATE
136 },
137 {
138 "item_armor_combat",
139 "models/powerups/armor/armor_yel.md3",
140 "",
141 "sound/misc/ar2_pkup.wav",
142 "icons/iconr_yellow",
143 "Armor",
144 50,
145 ARMOR,
146 SUB_NONE,
147 SPECIAL_SFX_ROTATE
148 },
149 {
150 "item_armor_shard",
151 "models/powerups/armor/shard.md3",
152 "",
153 "sound/misc/ar1_pkup.wav",
154 "icons/iconr_shard",
155 "Armor Shared",
156 5,
157 ARMOR,
158 SUB_NONE,
159 SPECIAL_SFX_ROTATE
160 },
161 {
162 "weapon_gauntlet",
163 "models/weapons2/gauntlet/gauntlet.md3",
164 "",
165 "sound/misc/w_pkup.wav",
166 "icons/iconw_gauntlet",
167 "Gauntlet",
168 0,
169 WEAPON,
170 GAUNTLET,
171 SPECIAL_SFX_ROTATE
172 },
173 {
174 "weapon_shotgun",
175 "models/weapons2/shotgun/shotgun.md3",
176 "",
177 "sound/misc/w_pkup.wav",
178 "icons/iconw_shotgun",
179 "Shotgun",
180 10,
181 WEAPON,
182 SHOTGUN,
183 SPECIAL_SFX_ROTATE
184 },
185 {
186 "weapon_machinegun",
187 "models/weapons2/machinegun/machinegun.md3",
188 "",
189 "sound/misc/w_pkup.wav",
190 "icons/iconw_machinegun",
191 "Machinegun",
192 40,
193 WEAPON,
194 MACHINEGUN,
195 SPECIAL_SFX_ROTATE
196 },
197 {
198 "weapon_grenadelauncher",
199 "models/weapons2/grenadel/grenadel.md3",
200 "",
201 "sound/misc/w_pkup.wav",
202 "icons/iconw_grenade",
203 "Grenade Launcher",
204 10,
205 WEAPON,
206 GRENADE_LAUNCHER,
207 SPECIAL_SFX_ROTATE
208 },
209 {
210 "weapon_rocketlauncher",
211 "models/weapons2/rocketl/rocketl.md3",
212 "",
213 "sound/misc/w_pkup.wav",
214 "icons/iconw_rocket",
215 "Rocket Launcher",
216 10,
217 WEAPON,
218 ROCKET_LAUNCHER,
219 SPECIAL_SFX_ROTATE
220 },
221 {
222 "weapon_lightning",
223 "models/weapons2/lightning/lightning.md3",
224 "",
225 "sound/misc/w_pkup.wav",
226 "icons/iconw_lightning",
227 "Lightning Gun",
228 100,
229 WEAPON,
230 LIGHTNING,
231 SPECIAL_SFX_ROTATE
232 },
233 {
234 "weapon_railgun",
235 "models/weapons2/railgun/railgun.md3",
236 "",
237 "sound/misc/w_pkup.wav",
238 "icons/iconw_railgun",
239 "Railgun",
240 10,
241 WEAPON,
242 RAILGUN,
243 SPECIAL_SFX_ROTATE
244 },
245 {
246 "weapon_plasmagun",
247 "models/weapons2/plasma/plasma.md3",
248 "",
249 "sound/misc/w_pkup.wav",
250 "icons/iconw_plasma",
251 "Plasma Gun",
252 50,
253 WEAPON,
254 PLASMAGUN,
255 SPECIAL_SFX_ROTATE
256 },
257 {
258 "weapon_bfg",
259 "models/weapons2/bfg/bfg.md3",
260 "",
261 "sound/misc/w_pkup.wav",
262 "icons/iconw_bfg",
263 "BFG10K",
264 20,
265 WEAPON,
266 BFG,
267 SPECIAL_SFX_ROTATE
268 },
269 {
270 "weapon_grapplinghook",
271 "models/weapons2/grapple/grapple.md3",
272 "",
273 "sound/misc/w_pkup.wav",
274 "icons/iconw_grapple",
275 "Grappling Hook",
276 0,
277 WEAPON,
278 GRAPPLING_HOOK,
279 SPECIAL_SFX_ROTATE
280 },
281 {
282 0
283 }
284
285 };
286
287
288 /*!
289 */
getItemElement(const stringc & key)290 const SItemElement * getItemElement ( const stringc& key )
291 {
292 const SItemElement *item = Quake3ItemElement;
293
294 while ( item->key )
295 {
296 if ( 0 == strcmp ( key.c_str(), item->key ) )
297 return item;
298 item += 1;
299 }
300 return 0;
301 }
302
303 /*!
304 Quake3 Model Factory.
305 Takes the mesh buffers and creates scenenodes for their associated shaders
306 */
Q3ShaderFactory(Q3LevelLoadParameter & loadParam,IrrlichtDevice * device,IQ3LevelMesh * mesh,eQ3MeshIndex meshIndex,ISceneNode * parent,IMetaTriangleSelector * meta,bool showShaderName)307 void Q3ShaderFactory ( Q3LevelLoadParameter &loadParam,
308 IrrlichtDevice *device,
309 IQ3LevelMesh* mesh,
310 eQ3MeshIndex meshIndex,
311 ISceneNode *parent,
312 IMetaTriangleSelector *meta,
313 bool showShaderName )
314 {
315 if ( 0 == mesh || 0 == device )
316 return;
317
318 IMeshSceneNode* node = 0;
319 ISceneManager* smgr = device->getSceneManager();
320 ITriangleSelector * selector = 0;
321
322 // the additional mesh can be quite huge and is unoptimized
323 // Save to cast to SMesh
324 SMesh * additional_mesh = (SMesh*) mesh->getMesh ( meshIndex );
325 if ( 0 == additional_mesh || additional_mesh->getMeshBufferCount() == 0)
326 return;
327
328 char buf[128];
329 if ( loadParam.verbose > 0 )
330 {
331 loadParam.startTime = device->getTimer()->getRealTime();
332 if ( loadParam.verbose > 1 )
333 {
334 snprintf(buf, 128, "q3shaderfactory start" );
335 device->getLogger()->log( buf, ELL_INFORMATION);
336 }
337 }
338
339 IGUIFont *font = 0;
340 if ( showShaderName )
341 font = device->getGUIEnvironment()->getFont("fontlucida.png");
342
343 IVideoDriver *driver = device->getVideoDriver();
344
345 // create helper textures
346 if ( 1 )
347 {
348 tTexArray tex;
349 u32 pos = 0;
350 getTextures ( tex, "$redimage $blueimage $whiteimage $checkerimage", pos,
351 device->getFileSystem(), driver );
352 }
353
354 s32 sceneNodeID = 0;
355 for ( u32 i = 0; i!= additional_mesh->getMeshBufferCount (); ++i )
356 {
357 IMeshBuffer *meshBuffer = additional_mesh->getMeshBuffer ( i );
358 const SMaterial &material = meshBuffer->getMaterial();
359
360 //! The ShaderIndex is stored in the second material parameter
361 s32 shaderIndex = (s32) material.MaterialTypeParam2;
362
363 // the meshbuffer can be rendered without additional support, or it has no shader
364 IShader *shader = (IShader *) mesh->getShader ( shaderIndex );
365
366 // no shader, or mapped to existing material
367 if ( 0 == shader )
368 {
369
370 #if 1
371 // clone mesh
372 SMesh * m = new SMesh ();
373 m->addMeshBuffer ( meshBuffer );
374 SMaterial &mat = m->getMeshBuffer( 0 )->getMaterial();
375 if ( mat.getTexture( 0 ) == 0 )
376 mat.setTexture ( 0, driver->getTexture ( "$blueimage" ) );
377 if ( mat.getTexture( 1 ) == 0 )
378 mat.setTexture ( 1, driver->getTexture ( "$redimage" ) );
379
380 IMesh * store = smgr->getMeshManipulator ()->createMeshWith2TCoords ( m );
381 m->drop();
382
383 node = smgr->addMeshSceneNode ( store, parent, sceneNodeID );
384 node->setAutomaticCulling ( scene::EAC_OFF );
385 store->drop ();
386 sceneNodeID += 1;
387 #endif
388 }
389 else if ( 1 )
390 {
391 /*
392 stringc s;
393 dumpShader ( s, shader );
394 printf ( s.c_str () );
395 */
396 // create sceneNode
397 node = smgr->addQuake3SceneNode ( meshBuffer, shader, parent, sceneNodeID );
398 node->setAutomaticCulling ( scene::EAC_FRUSTUM_BOX );
399 sceneNodeID += 1;
400 }
401
402 // show Debug Shader Name
403 if ( showShaderName && node )
404 {
405 swprintf ( (wchar_t*) buf, 64, L"%hs:%d", node->getName(),node->getID() );
406 smgr->addBillboardTextSceneNode(
407 font,
408 (wchar_t*) buf,
409 node,
410 dimension2d<f32>(80.0f, 8.0f),
411 vector3df(0, 10, 0),
412 sceneNodeID);
413 sceneNodeID += 1;
414 }
415
416 // create Portal Rendertargets
417 if ( shader )
418 {
419 const SVarGroup *group = shader->getGroup(1);
420 if ( group->isDefined( "surfaceparm", "portal" ) )
421 {
422 }
423
424 }
425
426
427 // add collision
428 // find out if shader is marked as nonsolid
429 u8 doCreate = meta !=0 ;
430
431 if ( shader )
432 {
433 const SVarGroup *group = shader->getGroup(1);
434 if ( group->isDefined( "surfaceparm", "trans" )
435 // || group->isDefined( "surfaceparm", "sky" )
436 // || group->isDefined( "surfaceparm", "nonsolid" )
437 )
438 {
439 if ( !group->isDefined( "surfaceparm", "metalsteps" ) )
440 {
441 doCreate = 0;
442 }
443 }
444 }
445
446 if ( doCreate )
447 {
448 IMesh *m = 0;
449
450 //! controls if triangles are modified by the scenenode during runtime
451 bool takeOriginal = true;
452
453 if ( takeOriginal )
454 {
455 m = new SMesh ();
456 ((SMesh*) m )->addMeshBuffer (meshBuffer);
457 }
458 else
459 {
460 m = node->getMesh();
461 }
462
463 //selector = smgr->createOctreeTriangleSelector ( m, 0, 128 );
464 selector = smgr->createTriangleSelector ( m, 0 );
465 meta->addTriangleSelector ( selector );
466 selector->drop ();
467
468 if ( takeOriginal )
469 {
470 delete m;
471 }
472 }
473
474 }
475
476 #if 0
477 if ( meta )
478 {
479 selector = smgr->createOctreeTriangleSelector ( additional_mesh, 0 );
480 meta->addTriangleSelector ( selector );
481 selector->drop ();
482 }
483 #endif
484
485 if ( loadParam.verbose > 0 )
486 {
487 loadParam.endTime = device->getTimer()->getRealTime ();
488 snprintf(buf, 128, "q3shaderfactory needed %04d ms to create %d shader nodes",
489 loadParam.endTime - loadParam.startTime,
490 sceneNodeID
491 );
492 device->getLogger()->log(buf, ELL_INFORMATION);
493 }
494
495 }
496
497
498 /*!
499 create Items from Entity
500 */
Q3ModelFactory(Q3LevelLoadParameter & loadParam,IrrlichtDevice * device,IQ3LevelMesh * masterMesh,ISceneNode * parent,bool showShaderName)501 void Q3ModelFactory ( Q3LevelLoadParameter &loadParam,
502 IrrlichtDevice *device,
503 IQ3LevelMesh* masterMesh,
504 ISceneNode *parent,
505 bool showShaderName
506 )
507 {
508 if ( 0 == masterMesh )
509 return;
510
511 tQ3EntityList &entity = masterMesh->getEntityList ();
512 ISceneManager* smgr = device->getSceneManager();
513
514
515 char buf[128];
516 const SVarGroup *group;
517 IEntity search;
518 s32 index;
519 s32 lastIndex;
520
521 /*
522 stringc s;
523 FILE *f = 0;
524 f = fopen ( "entity.txt", "wb" );
525 for ( index = 0; (u32) index < entityList.size (); ++index )
526 {
527 const IEntity *entity = &entityList[ index ];
528 s = entity->name;
529 dumpShader ( s, entity );
530 fwrite ( s.c_str(), 1, s.size(), f );
531 }
532 fclose ( f );
533 */
534 IAnimatedMeshMD3* model;
535 SMD3Mesh * mesh;
536 const SMD3MeshBuffer *meshBuffer;
537 IMeshSceneNode* node;
538 ISceneNodeAnimator* anim;
539 const IShader *shader;
540 u32 pos;
541 vector3df p;
542 u32 nodeCount = 0;
543 tTexArray textureArray;
544
545 IGUIFont *font = 0;
546 if ( showShaderName )
547 font = device->getGUIEnvironment()->getFont("fontlucida.png");
548
549 const SItemElement *itemElement;
550
551 // walk list
552 for ( index = 0; (u32) index < entity.size(); ++index )
553 {
554 itemElement = getItemElement ( entity[index].name );
555 if ( 0 == itemElement )
556 continue;
557
558 pos = 0;
559 p = getAsVector3df ( entity[index].getGroup(1)->get ( "origin" ), pos );
560
561 nodeCount += 1;
562 for ( u32 g = 0; g < 2; ++g )
563 {
564 if ( 0 == itemElement->model[g] || itemElement->model[g][0] == 0 )
565 continue;
566 model = (IAnimatedMeshMD3*) smgr->getMesh( itemElement->model[g] );
567 if ( 0 == model )
568 continue;
569
570 mesh = model->getOriginalMesh();
571 for ( u32 j = 0; j != mesh->Buffer.size (); ++j )
572 {
573 meshBuffer = mesh->Buffer[j];
574 if ( 0 == meshBuffer )
575 continue;
576
577 shader = masterMesh->getShader ( meshBuffer->Shader.c_str(), false );
578 IMeshBuffer *final = model->getMesh(0)->getMeshBuffer(j);
579 if ( shader )
580 {
581 //!TODO: Hack don't modify the vertexbuffer. make it better;-)
582 final->getMaterial().ColorMask = 0;
583 node = smgr->addQuake3SceneNode ( final, shader, parent );
584 final->getMaterial().ColorMask = 15;
585 }
586 else
587 {
588 // clone mesh
589 SMesh * m = new SMesh ();
590 m->addMeshBuffer ( final );
591 node = smgr->addMeshSceneNode ( m, parent );
592 m->drop();
593 }
594
595 if ( 0 == node )
596 {
597 snprintf ( buf, 128, "q3ModelFactory shader %s failed", meshBuffer->Shader.c_str() );
598 device->getLogger()->log ( buf );
599 continue;
600 }
601
602 // node was maybe centered by shaderscenenode
603 node->setPosition ( p );
604 node->setName ( meshBuffer->Shader );
605 node->setAutomaticCulling ( scene::EAC_BOX );
606
607 // add special effects to node
608 if ( itemElement->special & SPECIAL_SFX_ROTATE ||
609 (g == 0 && itemElement->special & SPECIAL_SFX_ROTATE_1)
610 )
611 {
612 anim = smgr->createRotationAnimator ( vector3df ( 0.f,
613 2.f, 0.f ) );
614 node->addAnimator ( anim );
615 anim->drop ();
616 }
617
618 if ( itemElement->special & SPECIAL_SFX_BOUNCE )
619 {
620 //anim = smgr->createFlyStraightAnimator (
621 // p, p + vector3df ( 0.f, 60.f, 0.f ), 1000, true, true );
622 anim = smgr->createFlyCircleAnimator (
623 p + vector3df( 0.f, 20.f, 0.f ),
624 20.f,
625 0.005f,
626 vector3df ( 1.f, 0.f, 0.f ),
627 core::fract ( nodeCount * 0.05f ),
628 1.f
629 );
630 node->addAnimator ( anim );
631 anim->drop ();
632 }
633 }
634 }
635 // show name
636 if ( showShaderName )
637 {
638 swprintf ( (wchar_t*) buf, sizeof(buf) / 2, L"%hs", itemElement->key );
639 smgr->addBillboardTextSceneNode(
640 font,
641 (wchar_t*) buf,
642 parent,
643 dimension2d<f32>(80.0f, 8.0f),
644 p + vector3df(0, 30, 0),
645 0);
646 }
647 }
648
649 // music
650 search.name = "worldspawn";
651 index = entity.binary_search_multi ( search, lastIndex );
652
653 if ( index >= 0 )
654 {
655 group = entity[ index ].getGroup(1);
656 background_music ( group->get ( "music" ).c_str () );
657 }
658
659 // music
660 search.name = "worldspawn";
661 index = entity.binary_search_multi ( search, lastIndex );
662
663 if ( index >= 0 )
664 {
665 group = entity[ index ].getGroup(1);
666 background_music ( group->get ( "music" ).c_str () );
667 }
668
669 //IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
670 //IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
671
672 }
673
674 /*!
675 so we need a good starting Position in the level.
676 we can ask the Quake3 Loader for all entities with class_name "info_player_deathmatch"
677 */
Q3StartPosition(IQ3LevelMesh * mesh,ICameraSceneNode * camera,s32 startposIndex,const vector3df & translation)678 s32 Q3StartPosition ( IQ3LevelMesh* mesh,
679 ICameraSceneNode* camera,
680 s32 startposIndex,
681 const vector3df &translation
682 )
683 {
684 if ( 0 == mesh )
685 return 0;
686
687 tQ3EntityList &entityList = mesh->getEntityList ();
688
689 IEntity search;
690 search.name = "info_player_start"; // "info_player_deathmatch";
691
692 // find all entities in the multi-list
693 s32 lastIndex;
694 s32 index = entityList.binary_search_multi ( search, lastIndex );
695
696 if ( index < 0 )
697 {
698 search.name = "info_player_deathmatch";
699 index = entityList.binary_search_multi ( search, lastIndex );
700 }
701
702 if ( index < 0 )
703 return 0;
704
705 index += core::clamp ( startposIndex, 0, lastIndex - index );
706
707 u32 parsepos;
708
709 const SVarGroup *group;
710 group = entityList[ index ].getGroup(1);
711
712 parsepos = 0;
713 vector3df pos = getAsVector3df ( group->get ( "origin" ), parsepos );
714 pos += translation;
715
716 parsepos = 0;
717 f32 angle = getAsFloat ( group->get ( "angle"), parsepos );
718
719 vector3df target ( 0.f, 0.f, 1.f );
720 target.rotateXZBy ( angle - 90.f, vector3df () );
721
722 if ( camera )
723 {
724 camera->setPosition ( pos );
725 camera->setTarget ( pos + target );
726 //! New. FPSCamera and animators catches reset on animate 0
727 camera->OnAnimate ( 0 );
728 }
729 return lastIndex - index + 1;
730 }
731
732
733 /*!
734 gets a accumulated force on a given surface
735 */
getGravity(const c8 * surface)736 vector3df getGravity ( const c8 * surface )
737 {
738 if ( 0 == strcmp ( surface, "earth" ) ) return vector3df ( 0.f, -90.f, 0.f );
739 if ( 0 == strcmp ( surface, "moon" ) ) return vector3df ( 0.f, -6.f / 100.f, 0.f );
740 if ( 0 == strcmp ( surface, "water" ) ) return vector3df ( 0.1f / 100.f, -2.f / 100.f, 0.f );
741 if ( 0 == strcmp ( surface, "ice" ) ) return vector3df ( 0.2f / 100.f, -9.f / 100.f, 0.3f / 100.f );
742
743 return vector3df ( 0.f, 0.f, 0.f );
744 }
745
746
747
748 /*
749 Dynamically load the Irrlicht Library
750 */
751
752 #if defined(_IRR_WINDOWS_API_)
753 #ifdef _MSC_VER
754 #pragma comment(lib, "Irrlicht.lib")
755 #endif
756
757 #include <windows.h>
758
load_createDevice(const c8 * filename)759 funcptr_createDevice load_createDevice ( const c8 * filename)
760 {
761 return (funcptr_createDevice) GetProcAddress ( LoadLibrary ( filename ), "createDevice" );
762 }
763
load_createDeviceEx(const c8 * filename)764 funcptr_createDeviceEx load_createDeviceEx ( const c8 * filename)
765 {
766 return (funcptr_createDeviceEx) GetProcAddress ( LoadLibrary ( filename ), "createDeviceEx" );
767 }
768
769 #else
770
771 // TODO: Dynamic Loading for other os
load_createDevice(const c8 * filename)772 funcptr_createDevice load_createDevice ( const c8 * filename)
773 {
774 return createDevice;
775 }
776
load_createDeviceEx(const c8 * filename)777 funcptr_createDeviceEx load_createDeviceEx ( const c8 * filename)
778 {
779 return createDeviceEx;
780 }
781
782 #endif
783
784 /*
785 get the current collision response camera animator
786 */
camCollisionResponse(IrrlichtDevice * device)787 ISceneNodeAnimatorCollisionResponse* camCollisionResponse( IrrlichtDevice * device )
788 {
789 ICameraSceneNode *camera = device->getSceneManager()->getActiveCamera();
790 ISceneNodeAnimatorCollisionResponse *a = 0;
791
792 list<ISceneNodeAnimator*>::ConstIterator it = camera->getAnimators().begin();
793 for (; it != camera->getAnimators().end(); ++it)
794 {
795 a = (ISceneNodeAnimatorCollisionResponse*) (*it);
796 if ( a->getType() == ESNAT_COLLISION_RESPONSE )
797 return a;
798 }
799
800 return 0;
801 }
802
803
804 //! internal Animation
setTimeFire(TimeFire * t,u32 delta,u32 flags)805 void setTimeFire ( TimeFire *t, u32 delta, u32 flags )
806 {
807 t->flags = flags;
808 t->next = 0;
809 t->delta = delta;
810 }
811
812
checkTimeFire(TimeFire * t,u32 listSize,u32 now)813 void checkTimeFire ( TimeFire *t, u32 listSize, u32 now )
814 {
815 u32 i;
816 for ( i = 0; i < listSize; ++i )
817 {
818 if ( now < t[i].next )
819 continue;
820
821 t[i].next = core::max_ ( now + t[i].delta, t[i].next + t[i].delta );
822 t[i].flags |= FIRED;
823 }
824 }
825