Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/mount_points/src/world_entities/world_entity.cc @ 10052

Last change on this file since 10052 was 10052, checked in by patrick, 17 years ago

integrating loading to we

File size: 19.9 KB
Line 
1
2
3/*
4   orxonox - the future of 3D-vertical-scrollers
5
6   Copyright (C) 2004 orx
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2, or (at your option)
11   any later version.
12
13   ### File Specific:
14   main-programmer: Patrick Boenzli
15   co-programmer: Christian Meyer
16*/
17#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WORLD_ENTITY
18
19#include "world_entity.h"
20#include "shell_command.h"
21
22#include "util/loading/resource_manager.h"
23#include "resource_obj.h"
24#include "md2/md2Model.h"
25#include "md3/md3_model.h"
26#include "oif/object_information_file.h"
27
28#include "aabb_tree_node.h"
29
30#include "util/loading/load_param.h"
31#include "obb_tree.h"
32
33#include "elements/glgui_energywidget.h"
34
35#include "state.h"
36#include "camera.h"
37
38#include "collision_filter.h"
39#include "collision_event.h"
40#include "game_rules.h"
41#include "kill.h"
42#include "debug.h"
43
44#include "projectiles/projectile.h"
45
46SHELL_COMMAND(model, WorldEntity, loadModel)
47->describe("sets the Model of the WorldEntity")
48->defaultValues("models/ships/fighter.obj", 1.0f);
49
50SHELL_COMMAND(debugEntity, WorldEntity, debugWE);
51
52
53ObjectListDefinition(WorldEntity);
54/**
55 *  Loads the WordEntity-specific Part of any derived Class
56 *
57 * @param root: Normally NULL, as the Derived Entities define a loadParams Function themeselves,
58 *              that can calls WorldEntities loadParams for itself.
59 */
60WorldEntity::WorldEntity()
61  : Synchronizeable(), _collisionFilter(this)
62{
63  this->registerObject(this, WorldEntity::_objectList);
64
65  this->obbTree = NULL;
66  this->aabbNode = NULL;
67  this->healthWidget = NULL;
68  this->healthMax = 1.0f;
69  this->health = 1.0f;
70  this->damage = 0.0f; // no damage dealt by a default entity
71  this->scaling = 1.0f;
72
73  /* OSOLETE */
74  this->bVisible = true;
75  this->bCollide = true;
76
77  this->objectListNumber = OM_INIT;
78  this->lastObjectListNumber = OM_INIT;
79
80  this->_bOnGround = false;
81
82  // registering default reactions:
83  this->subscribeReaction(CoRe::CREngine::CR_OBJECT_DAMAGE, Projectile::staticClassID());
84
85  this->toList(OM_NULL);
86
87  this->registerVar( new SynchronizeableString( &this->md2TextureFileName, &this->md2TextureFileName, "md2TextureFileName", PERMISSION_MASTER_SERVER ) );
88  this->modelFileName_handle = registerVarId( new SynchronizeableString( &modelFileName, &modelFileName, "modelFileName", PERMISSION_MASTER_SERVER ) );
89  this->scaling_handle = registerVarId( new SynchronizeableFloat( &scaling, &scaling, "scaling", PERMISSION_MASTER_SERVER ) );
90  this->list_handle = registerVarId( new SynchronizeableInt( (int*)&objectListNumber, &list_write, "list", PERMISSION_MASTER_SERVER ) );
91
92  this->health_handle = registerVarId( new SynchronizeableFloat( &this->health, &this->health_write, "health", PERMISSION_MASTER_SERVER ) );
93  this->healthMax_handle = registerVarId( new SynchronizeableFloat( &this->healthMax, &this->healthMax_write, "maxHealth", PERMISSION_MASTER_SERVER ) );
94}
95
96/**
97 *  standard destructor
98*/
99WorldEntity::~WorldEntity ()
100{
101  State::getObjectManager()->toList(this, OM_INIT);
102
103  // Delete the model (unregister it with the ResourceManager)
104  for (unsigned int i = 0; i < this->models.size(); i++)
105    this->setModel(NULL, i);
106
107  // Delete the obbTree
108  if( this->obbTree != NULL)
109    delete this->obbTree;
110
111  if (this->healthWidget != NULL)
112    delete this->healthWidget;
113
114  this->unsubscribeReactions();
115}
116
117/**
118 * loads the WorldEntity Specific Parameters.
119 * @param root: the XML-Element to load the Data From
120 */
121void WorldEntity::loadParams(const TiXmlElement* root)
122{
123  // Do the PNode loading stuff
124  PNode::loadParams(root);
125
126  LoadParam(root, "md2texture", this, WorldEntity, loadMD2Texture)
127  .describe("the fileName of the texture, that should be loaded onto this world-entity. (must be relative to the data-dir)")
128  .defaultValues("");
129
130  // Model Loading
131  LoadParam(root, "model", this, WorldEntity, loadModel)
132  .describe("the fileName of the model, that should be loaded onto this world-entity. (must be relative to the data-dir)")
133  .defaultValues("", 1.0f, 0);
134
135  LoadParam(root, "maxHealth", this, WorldEntity, setHealthMax)
136  .describe("The Maximum health that can be loaded onto this entity")
137  .defaultValues(1.0f);
138
139  LoadParam(root, "health", this, WorldEntity, setHealth)
140  .describe("The Health the WorldEntity has at this moment")
141  .defaultValues(1.0f);
142
143  LoadParam(root, "list", this, WorldEntity, toListS);
144}
145
146
147/**
148 * loads a Model onto a WorldEntity
149 * @param fileName the name of the model to load
150 * @param scaling the Scaling of the model
151 *
152 * FIXME
153 * @todo: separate the obb tree generation from the model
154 */
155void WorldEntity::loadModel(const std::string& fileName, float scaling, unsigned int modelNumber, unsigned int obbTreeDepth)
156{
157  this->modelLODName = fileName;
158  this->scaling = scaling;
159
160  std::string name = fileName;
161
162  if (  name.find( Resources::ResourceManager::getInstance()->mainGlobalPath().name() ) == 0 )
163  {
164    name.erase(Resources::ResourceManager::getInstance()->mainGlobalPath().name().size());
165  }
166
167  this->modelFileName = name;
168
169  if (!fileName.empty())
170  {
171    // search for the special character # in the LoadParam
172    if (fileName.find('#') != std::string::npos)
173    {
174      PRINTF(4)("Found # in %s... searching for LOD's\n", fileName.c_str());
175      std::string lodFile = fileName;
176      unsigned int offset = lodFile.find('#');
177      for (unsigned int i = 0; i < 3; i++)
178      {
179        lodFile[offset] = 48+(int)i;
180        if (Resources::ResourceManager::getInstance()->checkFileInMainPath( lodFile))
181          this->loadModel(lodFile, scaling, i);
182      }
183      return;
184    }
185    if (this->scaling <= 0.0)
186    {
187      PRINTF(1)("YOU GAVE ME A CRAPY SCALE resetting to 1.0\n");
188      this->scaling = 1.0;
189    }
190    /// LOADING AN OBJ FILE
191    if(fileName.find(".obj") != std::string::npos)
192    {
193      PRINTF(4)("fetching OBJ file: %s\n", fileName.c_str());
194      // creating the model and loading it
195      StaticModel* model = new StaticModel();
196      *model = ResourceOBJ(fileName, this->scaling);
197      if (model->getVertexCount() > 0)
198      {
199        this->setModel(model, modelNumber);
200        if( modelNumber == 0 /* FIXME && !this->isA(CL_WEAPON) */)
201          this->buildObbTree(obbTreeDepth);
202      }
203      else
204        delete model;
205
206      // now get the object information file for this model, if any
207      std::string oifName = fileName.substr(0, fileName.length() - 3) + ".oif";
208      this->loadObjectInformationFile( oifName);
209    }
210    /// LOADING AN MD2-model
211    else if(fileName.find(".md2") != std::string::npos)
212    {
213      PRINTF(4)("fetching MD2 file: %s\n", fileName.c_str());
214      Model* m = new MD2Model(fileName, this->md2TextureFileName, this->scaling);
215      //this->setModel((Model*)ResourceManager::getInstance()->load(fileName, MD2, RP_CAMPAIGN), 0);
216      this->setModel(m, 0);
217
218      if( m != NULL)
219        this->buildObbTree(obbTreeDepth);
220    }
221    /// LOADING AN MD3-MODEL.
222    else if(fileName.find(".md3") != std::string::npos)
223    {
224      PRINTF(4)("fetching MD3 file: %s\n", fileName.c_str());
225//      Model* m = new md3::MD3Model(fileName, this->scaling);
226//      this->setModel(m, 0);
227
228      //       if( m != NULL)
229      //         this->buildObbTree(obbTreeDepth);
230    }
231  }
232  else
233  {
234    this->setModel(NULL);
235  }
236}
237
238/**
239 * sets a specific Model for the Object.
240 * @param model The Model to set
241 * @param modelNumber the n'th model in the List to get.
242 */
243void WorldEntity::setModel(Model* model, unsigned int modelNumber)
244{
245  if (this->models.size() <= modelNumber)
246    this->models.resize(modelNumber+1, NULL);
247
248  if (this->models[modelNumber] != NULL)
249  {
250    delete this->models[modelNumber];
251  }
252
253  this->models[modelNumber] = model;
254}
255
256
257
258/**
259 * loads the object information file for this model
260 * @param fileName the name of the file
261 */
262void WorldEntity::loadObjectInformationFile(const std::string& fileName)
263{
264  PRINTF(0)("loading the oif File: %s", fileName.c_str());
265}
266
267
268/**
269 * builds the obb-tree
270 * @param depth the depth to calculate
271 */
272bool WorldEntity::buildObbTree(int depth)
273{
274  if( this->obbTree != NULL)
275  {
276    delete this->obbTree;
277    this->obbTree = NULL;
278  }
279
280  if (this->models[0] != NULL)
281    this->obbTree = new OBBTree(depth, models[0]->getModelInfo(), this);
282  else
283  {
284    PRINTF(1)("could not create obb-tree, because no model was loaded yet\n");
285    this->obbTree = NULL;
286    return false;
287  }
288
289
290  // create the axis aligned bounding box
291  if( this->aabbNode != NULL)
292  {
293    delete this->aabbNode;
294    this->aabbNode = NULL;
295  }
296
297  if( this->models[0] != NULL)
298  {
299    this->aabbNode = new AABBTreeNode();
300    this->aabbNode->spawnBVTree(this->models[0]);
301  }
302  else
303  {
304    PRINTF(1)("could not create aabb bounding box, because no model was loaded yet\n");
305    this->aabbNode = NULL;
306    return false;
307  }
308  return true;
309}
310
311
312/**
313 * subscribes this world entity to a collision reaction
314 *  @param type the type of reaction to subscribe to
315 *  @param target1 a filter target (classID)
316 */
317void WorldEntity::subscribeReaction(CoRe::CREngine::ReactionType type, const ClassID& target1)
318{
319  this->_collisionFilter.subscribeReaction(type, target1);
320}
321
322
323/**
324 * subscribes this world entity to a collision reaction
325 *  @param type the type of reaction to subscribe to
326 *  @param target1 a filter target (classID)
327 */
328void WorldEntity::subscribeReaction(CoRe::CREngine::ReactionType type, const ClassID& target1, const ClassID& target2)
329{
330  this->_collisionFilter.subscribeReaction(type, target1, target2);
331}
332
333
334/**
335 * subscribes this world entity to a collision reaction
336 *  @param type the type of reaction to subscribe to
337 *  @param target1 a filter target (classID)
338 */
339void WorldEntity::subscribeReaction(CoRe::CREngine::ReactionType type, const ClassID& target1, const ClassID& target2, const ClassID& target3)
340{
341  this->_collisionFilter.subscribeReaction(type, target1, target2, target3);
342}
343
344
345/**
346 * unsubscribes a specific reaction from the worldentity
347 *  @param type the reaction to unsubscribe
348 */
349void WorldEntity::unsubscribeReaction(CoRe::CREngine::ReactionType type)
350{
351  this->_collisionFilter.unsubscribeReaction(type);
352}
353
354
355/**
356 * unsubscribes all collision reactions
357 */
358void WorldEntity::unsubscribeReactions()
359{
360  this->_collisionFilter.unsubscribeReactions();
361}
362
363
364/**
365 * @brief moves this entity to the List OM_List
366 * @param list the list to set this Entity to.
367 *
368 * this is the same as a call to State::getObjectManager()->toList(entity , list);
369 * directly, but with an easier interface.
370 *
371 * @todo inline this (peut etre)
372 */
373void WorldEntity::toList(OM_LIST list)
374{
375  State::getObjectManager()->toList(this, list);
376}
377
378void WorldEntity::toListS(const std::string& listName)
379{
380  OM_LIST id = ObjectManager::StringToOMList(listName);
381  if (id != OM_NULL)
382    this->toList(id);
383  else
384    PRINTF(2)("List %s not found\n", listName.c_str());
385}
386
387
388void WorldEntity::toReflectionList()
389{
390  State::getObjectManager()->toReflectionList( this );
391}
392
393void removeFromReflectionList()
394{
395  /// TODO
396  ///  State::getObject
397}
398
399/**
400 * sets the character attributes of a worldentity
401 * @param character attributes
402 *
403 * these attributes don't have to be set, only use them, if you need them
404*/
405//void WorldEntity::setCharacterAttributes(CharacterAttributes* charAttr)
406//{}
407
408
409/**
410 *  this function is called, when two entities collide
411 * @param entity: the world entity with whom it collides
412 *
413 * Implement behaviour like damage application or other miscellaneous collision stuff in this function
414 */
415void WorldEntity::collidesWith(WorldEntity* entity, const Vector& location)
416{
417  /**
418   * THIS IS A DEFAULT COLLISION-Effect.
419   * IF YOU WANT TO CREATE A SPECIFIC COLLISION ON EACH OBJECT
420   * USE::
421   * if (entity->isA(CL_WHAT_YOU_ARE_LOOKING_FOR)) { printf "dothings"; };
422   *
423   * You can always define a default Action.... don't be affraid just test it :)
424   */
425  //  PRINTF(3)("collision %s vs %s @ (%f,%f,%f)\n", this->getClassCName(), entity->getClassCName(), location.x, location.y, location.z);
426}
427
428
429/**
430 *  this function is called, when two entities collide
431 * @param entity: the world entity with whom it collides
432 *
433 * Implement behaviour like damage application or other miscellaneous collision stuff in this function
434 */
435void WorldEntity::collidesWithGround(const Vector& location)
436{
437  PRINTF(0)("BSP_GROUND: %s collides \n", this->getClassCName() );
438}
439
440void WorldEntity::collidesWithGround(const Vector& feet, const Vector& ray_1, const Vector& ray_2)
441{
442
443  // PRINTF(0)("BSP_GROUND: Player collides \n", this->getClassCName() );
444
445  Vector v = this->getAbsDirX();
446  v.x *= 10.1;
447  v.y *= 10.1;
448  v.z *= 10.1;
449  Vector u = Vector(0.0,-20.0,0.0);
450
451
452  if(!(this->getAbsCoor().x == ray_2.x && this->getAbsCoor().y == ray_2.y && this->getAbsCoor().z == ray_2.z) )
453  {
454
455    this->setAbsCoor(ray_2 - v);
456
457  }
458  else
459  {
460    if(ray_1.x == this->getAbsCoor().x + v.x && ray_1.y == this->getAbsCoor().y + v.y + 0.1 && ray_1.z ==this->getAbsCoor().z + v.z)
461    {
462      this->setAbsCoor(feet -u );
463    }
464
465    this->setAbsCoor(ray_2 - v);
466
467  }
468
469
470}
471
472/**
473 *  this is called immediately after the Entity has been constructed, initialized and then Spawned into the World
474 *
475 */
476void WorldEntity::postSpawn ()
477{}
478
479
480/**
481 *  this method is called by the world if the WorldEntity leaves the game
482 */
483void WorldEntity::leaveWorld ()
484{}
485
486
487/**
488 * resets the WorldEntity to its initial values. eg. used for multiplayer games: respawning
489 */
490void WorldEntity::reset()
491{
492  this->setHealth( this->getHealthMax() );
493}
494
495/**
496 *  this method is called every frame
497 * @param time: the time in seconds that has passed since the last tick
498 *
499 * Handle all stuff that should update with time inside this method (movement, animation, etc.)
500*/
501void WorldEntity::tick(float time)
502{}
503
504
505/**
506 *  the entity is drawn onto the screen with this function
507 *
508 * This is a central function of an entity: call it to let the entity painted to the screen.
509 * Just override this function with whatever you want to be drawn.
510*/
511void WorldEntity::draw() const
512{
513  //PRINTF(0)("(%s::%s)\n", this->getClassCName(), this->getName());
514  //  assert(!unlikely(this->models.empty()));
515  {
516    glMatrixMode(GL_MODELVIEW);
517    glPushMatrix();
518
519    /* translate */
520    glTranslatef (this->getAbsCoor ().x,
521                  this->getAbsCoor ().y,
522                  this->getAbsCoor ().z);
523    Vector tmpRot = this->getAbsDir().getSpacialAxis();
524    glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z );
525
526
527    // This Draws the LOD's
528    float cameraDistance = State::getCamera()->distance(this);
529    if (cameraDistance > 30 && this->models.size() >= 3 && this->models[2] != NULL)
530    {
531      this->models[2]->draw();
532    }
533    else if (cameraDistance > 10 && this->models.size() >= 2 && this->models[1] != NULL)
534    {
535      this->models[1]->draw();
536    }
537    else if (this->models.size() >= 1 && this->models[0] != NULL)
538    {
539      this->models[0]->draw();
540    }
541
542    //     if( this->aabbNode != NULL)
543    //       this->aabbNode->drawBV(0, DRAW_BV_POLYGON, Vector(1, 0.6, 0.2), true);
544
545    glPopMatrix();
546  }
547}
548
549/**
550 * @param health the Health to add.
551 * @returns the health left (this->healthMax - health+this->health)
552 */
553float WorldEntity::increaseHealth(float health)
554{
555  this->health += health;
556  if (this->health > this->healthMax)
557  {
558    float retHealth = this->healthMax - this->health;
559    this->health = this->healthMax;
560    this->updateHealthWidget();
561    return retHealth;
562  }
563  this->updateHealthWidget();
564  return 0.0;
565}
566
567/**
568 * @param health the Health to be removed
569 * @returns 0.0 or the rest, that was not substracted (bellow 0.0)
570 */
571float WorldEntity::decreaseHealth(float health)
572{
573  this->health -= health;
574
575  if (this->health < 0)
576  {
577    float retHealth = -this->health;
578    this->health = 0.0f;
579    this->updateHealthWidget();
580    return retHealth;
581  }
582  this->updateHealthWidget();
583  return 0.0;
584
585}
586
587/**
588 * @param maxHealth the maximal health that can be loaded onto the entity.
589 */
590void WorldEntity::setHealthMax(float healthMax)
591{
592  this->healthMax = healthMax;
593  if (this->health > this->healthMax)
594  {
595    PRINTF(3)("new maxHealth is bigger as the old health. Did you really intend to do this for (%s::%s)\n", this->getClassCName(), this->getCName());
596    this->health = this->healthMax;
597  }
598  this->updateHealthWidget();
599}
600
601/**
602 * @brief creates the HealthWidget
603 *
604 * since not all entities need an HealthWidget, it is only created on request.
605 */
606void WorldEntity::createHealthWidget()
607{
608  if (this->healthWidget == NULL)
609  {
610    this->healthWidget = new OrxGui::GLGuiEnergyWidget();
611    this->healthWidget->setDisplayedName(std::string(this->getClassName()) + " Energy:");
612    this->healthWidget->setSize2D(30,400);
613    this->healthWidget->setAbsCoor2D(10,100);
614
615    this->updateHealthWidget();
616  }
617  else
618    PRINTF(3)("Allready created the HealthWidget for %s::%s\n", this->getClassCName(), this->getCName());
619}
620
621void WorldEntity::increaseHealthMax(float increaseHealth)
622{
623  this->healthMax += increaseHealth;
624  this->updateHealthWidget();
625}
626
627
628OrxGui::GLGuiWidget* WorldEntity::getHealthWidget()
629{
630  this->createHealthWidget();
631  return this->healthWidget;
632}
633
634/**
635 * @param visibility shows or hides the health-bar
636 * (creates the widget if needed)
637 */
638void WorldEntity::setHealthWidgetVisibilit(bool visibility)
639{
640  if (visibility)
641  {
642    if (this->healthWidget != NULL)
643      this->healthWidget->show();
644    else
645    {
646      this->createHealthWidget();
647      this->updateHealthWidget();
648      this->healthWidget->show();
649    }
650  }
651  else if (this->healthWidget != NULL)
652    this->healthWidget->hide();
653}
654
655
656/**
657 * hit the world entity with
658 *  @param damage damage to be dealt
659 */
660void WorldEntity::hit(float damage, WorldEntity* killer)
661{
662  this->decreaseHealth(damage);
663
664  PRINTF(5)("Hit me: %s::%s now only %f/%f health\n", this->getClassCName(), this->getCName(), this->getHealth(), this->getHealthMax());
665
666  if( this->getHealth() > 0)
667  {
668    // any small explosion animaitions
669  }
670  else
671  {
672    this->destroy( killer );
673  }
674}
675
676
677/**
678 * destoys the world entity
679 */
680void WorldEntity::destroy(WorldEntity* killer)
681{
682  this->toList(OM_DEAD);
683}
684
685
686/**
687 * @brief updates the HealthWidget
688 */
689void WorldEntity::updateHealthWidget()
690{
691  if (this->healthWidget != NULL)
692  {
693    this->healthWidget->setMaximum(this->healthMax);
694    this->healthWidget->setValue(this->health);
695  }
696}
697
698
699/**
700 * DEBUG-DRAW OF THE BV-Tree.
701 * @param depth What depth to draw
702 * @param drawMode the mode to draw this entity under
703 */
704void WorldEntity::drawBVTree(int depth, int drawMode) const
705{
706  glMatrixMode(GL_MODELVIEW);
707  glPushMatrix();
708  /* translate */
709  glTranslatef (this->getAbsCoor ().x,
710                this->getAbsCoor ().y,
711                this->getAbsCoor ().z);
712  /* rotate */
713  Vector tmpRot = this->getAbsDir().getSpacialAxis();
714  glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z );
715
716
717  if (this->obbTree)
718    this->obbTree->drawBV(depth, drawMode);
719
720
721  glPopMatrix();
722}
723
724
725/**
726 * Debug the WorldEntity
727 */
728void WorldEntity::debugEntity() const
729{
730  PRINT(0)("WorldEntity %s::%s  (DEBUG)\n", this->getClassCName(), this->getCName());
731  this->debugNode();
732  PRINT(0)("List: %s ; ModelCount %d - ", ObjectManager::OMListToString(this->objectListNumber).c_str(), this->models.size());
733  for (unsigned int i = 0; i < this->models.size(); i++)
734  {
735    if (models[i] != NULL)
736      PRINT(0)(" : %d:%s", i, this->models[i]->getCName());
737  }
738  PRINT(0)("\n");
739
740}
741
742
743/**
744 * handler for changes on registred vars
745 * @param id id's which changed
746 */
747void WorldEntity::varChangeHandler( std::list< int > & id )
748{
749  if ( std::find( id.begin(), id.end(), modelFileName_handle ) != id.end() ||
750       std::find( id.begin(), id.end(), scaling_handle ) != id.end()
751     )
752  {
753    loadModel( modelFileName, scaling );
754  }
755
756  if ( std::find( id.begin(), id.end(), list_handle ) != id.end() )
757  {
758    this->toList( (OM_LIST)list_write );
759  }
760
761  if ( std::find( id.begin(), id.end(), health_handle ) != id.end() )
762  {
763    this->setHealth( health_write );
764  }
765
766  if ( std::find( id.begin(), id.end(), healthMax_handle ) != id.end() )
767  {
768    this->setHealthMax( healthMax_write );
769  }
770
771  PNode::varChangeHandler( id );
772}
773
Note: See TracBrowser for help on using the repository browser.