Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/cr/src/world_entities/world_entity.cc @ 8124

Last change on this file since 8124 was 8124, checked in by patrick, 18 years ago

cr: some bugs found and solved, minor cleanup

File size: 19.7 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 "model.h"
23#include "md2Model.h"
24#include "util/loading/resource_manager.h"
25#include "util/loading/load_param.h"
26#include "vector.h"
27#include "obb_tree.h"
28
29#include "glgui_bar.h"
30
31#include "state.h"
32#include "camera.h"
33
34#include "collision_handle.h"
35#include "collision_event.h"
36
37#include <stdarg.h>
38
39
40using namespace std;
41
42SHELL_COMMAND(model, WorldEntity, loadModel)
43->describe("sets the Model of the WorldEntity")
44->defaultValues("models/ships/fighter.obj", 1.0f);
45
46SHELL_COMMAND(debugEntity, WorldEntity, debugWE);
47
48/**
49 *  Loads the WordEntity-specific Part of any derived Class
50 *
51 * @param root: Normally NULL, as the Derived Entities define a loadParams Function themeselves,
52 *              that can calls WorldEntities loadParams for itself.
53 */
54WorldEntity::WorldEntity()
55    : Synchronizeable()
56{
57  this->setClassID(CL_WORLD_ENTITY, "WorldEntity");
58
59  this->obbTree = NULL;
60  this->healthWidget = NULL;
61  this->healthMax = 1.0f;
62  this->health = 1.0f;
63  this->scaling = 1.0f;
64
65  /* OSOLETE */
66  this->bVisible = true;
67  this->bCollide = true;
68
69  this->objectListNumber = OM_INIT;
70  this->objectListIterator = NULL;
71
72  // reset all collision handles to NULL == unsubscribed state
73  for(int i = 0; i < CREngine::CR_NUMBER; ++i)
74    this->collisionHandles[i] = NULL;
75  this->bReactive = false;
76
77  // registering default reactions:
78  this->subscribeReaction(CREngine::CR_OBJECT_DAMAGE, CL_WORLD_ENTITY);
79  this->subscribeReaction(CREngine::CR_PHYSICS_GROUND, CL_TERRAIN);
80
81  this->toList(OM_NULL);
82}
83
84/**
85 *  standard destructor
86*/
87WorldEntity::~WorldEntity ()
88{
89  State::getObjectManager()->toList(this, OM_INIT);
90
91  // Delete the model (unregister it with the ResourceManager)
92  for (unsigned int i = 0; i < this->models.size(); i++)
93    this->setModel(NULL, i);
94
95  // Delete the obbTree
96  if( this->obbTree != NULL)
97    delete this->obbTree;
98
99  if (this->healthWidget != NULL)
100    delete this->healthWidget;
101
102  this->unsubscribeReaction();
103}
104
105/**
106 * loads the WorldEntity Specific Parameters.
107 * @param root: the XML-Element to load the Data From
108 */
109void WorldEntity::loadParams(const TiXmlElement* root)
110{
111  // Do the PNode loading stuff
112  PNode::loadParams(root);
113
114  LoadParam(root, "md2texture", this, WorldEntity, loadMD2Texture)
115  .describe("the fileName of the texture, that should be loaded onto this world-entity. (must be relative to the data-dir)")
116  .defaultValues("");
117
118  // Model Loading
119  LoadParam(root, "model", this, WorldEntity, loadModel)
120  .describe("the fileName of the model, that should be loaded onto this world-entity. (must be relative to the data-dir)")
121  .defaultValues("", 1.0f, 0);
122
123  LoadParam(root, "maxHealth", this, WorldEntity, setHealthMax)
124  .describe("The Maximum health that can be loaded onto this entity")
125  .defaultValues(1.0f);
126
127  LoadParam(root, "health", this, WorldEntity, setHealth)
128  .describe("The Health the WorldEntity has at this moment")
129  .defaultValues(1.0f);
130}
131
132
133/**
134 * loads a Model onto a WorldEntity
135 * @param fileName the name of the model to load
136 * @param scaling the Scaling of the model
137 *
138 * FIXME
139 * @todo: separate the obb tree generation from the model
140 */
141void WorldEntity::loadModel(const std::string& fileName, float scaling, unsigned int modelNumber, unsigned int obbTreeDepth)
142{
143  this->modelLODName = fileName;
144  this->scaling = scaling;
145  if (!fileName.empty())
146  {
147    // search for the special character # in the LoadParam
148    if (fileName.find('#') != std::string::npos)
149    {
150      PRINTF(4)("Found # in %s... searching for LOD's\n", fileName.c_str());
151      std::string lodFile = fileName;
152      unsigned int offset = lodFile.find('#');
153      for (unsigned int i = 0; i < 3; i++)
154      {
155        lodFile[offset] = 48+(int)i;
156        if (ResourceManager::isInDataDir(lodFile))
157          this->loadModel(lodFile, scaling, i);
158      }
159      return;
160    }
161    if (this->scaling <= 0.0)
162    {
163      PRINTF(1)("YOU GAVE ME A CRAPY SCALE resetting to 1.0\n");
164      this->scaling = 1.0;
165    }
166    if(fileName.find(".obj") != std::string::npos)
167    {
168      PRINTF(4)("fetching OBJ file: %s\n", fileName.c_str());
169      BaseObject* loadedModel = ResourceManager::getInstance()->load(fileName, OBJ, RP_CAMPAIGN, this->scaling);
170      if (loadedModel != NULL)
171        this->setModel(dynamic_cast<Model*>(loadedModel), modelNumber);
172      else
173        PRINTF(1)("OBJ-File %s not found.\n", fileName.c_str());
174
175      if( modelNumber == 0)
176        this->buildObbTree(obbTreeDepth);
177    }
178    else if(fileName.find(".md2") != std::string::npos)
179    {
180      PRINTF(4)("fetching MD2 file: %s\n", fileName.c_str());
181      Model* m = new MD2Model(fileName, this->md2TextureFileName, this->scaling);
182      //this->setModel((Model*)ResourceManager::getInstance()->load(fileName, MD2, RP_CAMPAIGN), 0);
183      this->setModel(m, 0);
184
185      if( m != NULL)
186        this->buildObbTree(obbTreeDepth);
187    }
188  }
189  else
190  {
191    this->setModel(NULL);
192  }
193}
194
195/**
196 * sets a specific Model for the Object.
197 * @param model The Model to set
198 * @param modelNumber the n'th model in the List to get.
199 */
200void WorldEntity::setModel(Model* model, unsigned int modelNumber)
201{
202  if (this->models.size() <= modelNumber)
203    this->models.resize(modelNumber+1, NULL);
204
205  if (this->models[modelNumber] != NULL)
206  {
207    Resource* resource = ResourceManager::getInstance()->locateResourceByPointer(dynamic_cast<BaseObject*>(this->models[modelNumber]));
208    if (resource != NULL)
209      ResourceManager::getInstance()->unload(resource, RP_LEVEL);
210    else
211    {
212      PRINTF(4)("Forcing model deletion\n");
213      delete this->models[modelNumber];
214    }
215  }
216
217  this->models[modelNumber] = model;
218
219
220  //   if (this->model != NULL)
221  //     this->buildObbTree(4);
222}
223
224
225/**
226 * builds the obb-tree
227 * @param depth the depth to calculate
228 */
229bool WorldEntity::buildObbTree(int depth)
230{
231  if (this->obbTree)
232    delete this->obbTree;
233
234  if (this->models[0] != NULL)
235  {
236    this->obbTree = new OBBTree(depth, models[0]->getModelInfo(), this);
237    return true;
238  }
239  else
240  {
241    PRINTF(1)("could not create obb-tree, because no model was loaded yet\n");
242    this->obbTree = NULL;
243    return false;
244  }
245}
246
247
248/**
249 * subscribes this world entity to a collision reaction
250 *  @param type the type of reaction to subscribe to
251 *  @param target1 a filter target (classID)
252 */
253void WorldEntity::subscribeReaction(CREngine::CRType type, long target1)
254{
255  this->subscribeReaction(type);
256
257  // add the target filter
258  this->collisionHandles[type]->addTarget(target1);
259}
260
261
262/**
263 * subscribes this world entity to a collision reaction
264 *  @param type the type of reaction to subscribe to
265 *  @param target1 a filter target (classID)
266 */
267void WorldEntity::subscribeReaction(CREngine::CRType type, long target1, long target2)
268{
269  this->subscribeReaction(type);
270
271  // add the target filter
272  this->collisionHandles[type]->addTarget(target1);
273  this->collisionHandles[type]->addTarget(target2);
274}
275
276
277/**
278 * subscribes this world entity to a collision reaction
279 *  @param type the type of reaction to subscribe to
280 *  @param target1 a filter target (classID)
281 */
282void WorldEntity::subscribeReaction(CREngine::CRType type, long target1, long target2, long target3)
283{
284  this->subscribeReaction(type);
285
286  // add the target filter
287  this->collisionHandles[type]->addTarget(target1);
288  this->collisionHandles[type]->addTarget(target2);
289  this->collisionHandles[type]->addTarget(target3);
290}
291
292
293/**
294 * subscribes this world entity to a collision reaction
295 *  @param type the type of reaction to subscribe to
296 *  @param target1 a filter target (classID)
297 */
298void WorldEntity::subscribeReaction(CREngine::CRType type, long target1, long target2, long target3, long target4)
299{
300  this->subscribeReaction(type);
301
302  // add the target filter
303  this->collisionHandles[type]->addTarget(target1);
304  this->collisionHandles[type]->addTarget(target2);
305  this->collisionHandles[type]->addTarget(target3);
306  this->collisionHandles[type]->addTarget(target4);
307}
308
309
310/**
311 * subscribes this world entity to a collision reaction
312 *  @param type the type of reaction to subscribe to
313 *  @param nrOfTargets number of target filters
314 *  @param ... the targets as classIDs
315 */
316void WorldEntity::subscribeReaction(CREngine::CRType type)
317{
318  if( this->collisionHandles[type] != NULL)  {
319    PRINTF(2)("Registering for a CollisionReaction already subscribed to! Skipping\n");
320    return;
321  }
322
323  this->collisionHandles[type] = CREngine::getInstance()->subscribeReaction(this, type);
324
325  // now there is at least one collision reaction subscribed
326  this->bReactive = true;
327}
328
329
330/**
331 * unsubscribes a specific reaction from the worldentity
332 *  @param type the reaction to unsubscribe
333 */
334void WorldEntity::unsubscribeReaction(CREngine::CRType type)
335{
336  if( this->collisionHandles[type] == NULL)
337    return;
338
339  CREngine::getInstance()->unsubscribeReaction(this->collisionHandles[type]);
340  this->collisionHandles[type] = NULL;
341
342  // check if there is still any handler registered
343  for(int i = 0; i < CREngine::CR_NUMBER; ++i)
344  {
345    if( this->collisionHandles[i] != NULL)
346    {
347      this->bReactive = true;
348      return;
349    }
350  }
351  this->bReactive = false;
352}
353
354
355/**
356 * unsubscribes all collision reactions
357 */
358void WorldEntity::unsubscribeReaction()
359{
360  for( int i = 0; i < CREngine::CR_NUMBER; i++)
361    this->unsubscribeReaction((CREngine::CRType)i);
362
363  // there are no reactions subscribed from now on
364  this->bReactive = false;
365}
366
367
368/**
369 * registers a new collision to this world entity
370 *  @param collisionEvent the event
371 */
372bool WorldEntity::registerCollision(WorldEntity* entityA, WorldEntity* entityB, BoundingVolume* bvA, BoundingVolume* bvB)
373{
374  // is there any handler listening?
375  if( !this->bReactive)
376    return false;
377
378  PRINTF(0)("Registering Collision: %s vs %s \n", entityA->getClassName(), entityB->getClassName());
379  // create a collision event
380  CollisionEvent* c = CREngine::getInstance()->popCollisionEventObject();
381  assert(c != NULL); // if this should fail: we got not enough precached CollisionEvents: alter value in cr_defs.h
382  c->collide(entityA, entityB, bvA, bvB);
383
384  for( int i = 0; i < CREngine::CR_NUMBER; ++i)
385    if( this->collisionHandles[i] != NULL)
386      this->collisionHandles[i]->registerCollisionEvent(c);
387}
388
389
390/**
391 * @brief moves this entity to the List OM_List
392 * @param list the list to set this Entity to.
393 *
394 * this is the same as a call to State::getObjectManager()->toList(entity , list);
395 * directly, but with an easier interface.
396 *
397 * @todo inline this (peut etre)
398 */
399void WorldEntity::toList(OM_LIST list)
400{
401  State::getObjectManager()->toList(this, list);
402}
403
404
405
406/**
407 * sets the character attributes of a worldentity
408 * @param character attributes
409 *
410 * these attributes don't have to be set, only use them, if you need them
411*/
412//void WorldEntity::setCharacterAttributes(CharacterAttributes* charAttr)
413//{}
414
415
416/**
417 *  this function is called, when two entities collide
418 * @param entity: the world entity with whom it collides
419 *
420 * Implement behaviour like damage application or other miscellaneous collision stuff in this function
421 */
422void WorldEntity::collidesWith(WorldEntity* entity, const Vector& location)
423{
424  /**
425   * THIS IS A DEFAULT COLLISION-Effect.
426   * IF YOU WANT TO CREATE A SPECIFIC COLLISION ON EACH OBJECT
427   * USE::
428   * if (entity->isA(CL_WHAT_YOU_ARE_LOOKING_FOR)) { printf "dothings"; };
429   *
430   * You can always define a default Action.... don't be affraid just test it :)
431   */
432  //  PRINTF(3)("collision %s vs %s @ (%f,%f,%f)\n", this->getClassName(), entity->getClassName(), location.x, location.y, location.z);
433}
434
435
436/**
437 *  this is called immediately after the Entity has been constructed, initialized and then Spawned into the World
438 *
439 */
440void WorldEntity::postSpawn ()
441{}
442
443
444/**
445 *  this method is called by the world if the WorldEntity leaves the game
446 */
447void WorldEntity::leaveWorld ()
448{}
449
450
451/**
452 * resets the WorldEntity to its initial values. eg. used for multiplayer games: respawning
453 */
454void WorldEntity::reset()
455{}
456
457/**
458 *  this method is called every frame
459 * @param time: the time in seconds that has passed since the last tick
460 *
461 * Handle all stuff that should update with time inside this method (movement, animation, etc.)
462*/
463void WorldEntity::tick(float time)
464{}
465
466
467/**
468 *  the entity is drawn onto the screen with this function
469 *
470 * This is a central function of an entity: call it to let the entity painted to the screen.
471 * Just override this function with whatever you want to be drawn.
472*/
473void WorldEntity::draw() const
474{
475  //PRINTF(0)("(%s::%s)\n", this->getClassName(), this->getName());
476  //  assert(!unlikely(this->models.empty()));
477  {
478    glMatrixMode(GL_MODELVIEW);
479    glPushMatrix();
480
481    /* translate */
482    glTranslatef (this->getAbsCoor ().x,
483                  this->getAbsCoor ().y,
484                  this->getAbsCoor ().z);
485    Vector tmpRot = this->getAbsDir().getSpacialAxis();
486    glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z );
487
488
489    // This Draws the LOD's
490    float cameraDistance = State::getCamera()->distance(this);
491    if (cameraDistance > 30 && this->models.size() >= 3 && this->models[2] != NULL)
492    {
493      this->models[2]->draw();
494    }
495    else if (cameraDistance > 10 && this->models.size() >= 2 && this->models[1] != NULL)
496    {
497      this->models[1]->draw();
498    }
499    else if (this->models.size() >= 1 && this->models[0] != NULL)
500    {
501      this->models[0]->draw();
502    }
503    glPopMatrix();
504  }
505}
506
507/**
508 * @param health the Health to add.
509 * @returns the health left (this->healthMax - health+this->health)
510 */
511float WorldEntity::increaseHealth(float health)
512{
513  this->health += health;
514  if (this->health > this->healthMax)
515  {
516    float retHealth = this->healthMax - this->health;
517    this->health = this->healthMax;
518    this->updateHealthWidget();
519    return retHealth;
520  }
521  this->updateHealthWidget();
522  return 0.0;
523}
524
525/**
526 * @param health the Health to be removed
527 * @returns 0.0 or the rest, that was not substracted (bellow 0.0)
528 */
529float WorldEntity::decreaseHealth(float health)
530{
531  this->health -= health;
532
533  if (this->health < 0)
534  {
535    float retHealth = -this->health;
536    this->health = 0.0f;
537    this->updateHealthWidget();
538    return retHealth;
539  }
540  this->updateHealthWidget();
541  return 0.0;
542
543}
544
545/**
546 * @param maxHealth the maximal health that can be loaded onto the entity.
547 */
548void WorldEntity::setHealthMax(float healthMax)
549{
550  this->healthMax = healthMax;
551  if (this->health > this->healthMax)
552  {
553    PRINTF(3)("new maxHealth is bigger as the old health. Did you really intend to do this for (%s::%s)\n", this->getClassName(), this->getName());
554    this->health = this->healthMax;
555  }
556  this->updateHealthWidget();
557}
558
559/**
560 * @brief creates the HealthWidget
561 *
562 * since not all entities need an HealthWidget, it is only created on request.
563 */
564void WorldEntity::createHealthWidget()
565{
566  if (this->healthWidget == NULL)
567  {
568    this->healthWidget = new OrxGui::GLGuiBar();
569    this->healthWidget->setSize2D(30,400);
570    this->healthWidget->setAbsCoor2D(10,100);
571
572    this->updateHealthWidget();
573  }
574  else
575    PRINTF(3)("Allready created the HealthWidget for %s::%s\n", this->getClassName(), this->getName());
576}
577
578void WorldEntity::increaseHealthMax(float increaseHealth)
579{
580  this->healthMax += increaseHealth;
581  this->updateHealthWidget();
582}
583
584
585OrxGui::GLGuiWidget* WorldEntity::getHealthWidget()
586{
587  this->createHealthWidget();
588  return this->healthWidget;
589}
590
591/**
592 * @param visibility shows or hides the health-bar
593 * (creates the widget if needed)
594 */
595void WorldEntity::setHealthWidgetVisibilit(bool visibility)
596{
597  if (visibility)
598  {
599    if (this->healthWidget != NULL)
600      this->healthWidget->show();
601    else
602    {
603      this->createHealthWidget();
604      this->updateHealthWidget();
605      this->healthWidget->show();
606    }
607  }
608  else if (this->healthWidget != NULL)
609    this->healthWidget->hide();
610}
611
612/**
613 * @brief updates the HealthWidget
614 */
615void WorldEntity::updateHealthWidget()
616{
617  if (this->healthWidget != NULL)
618  {
619    this->healthWidget->setMaximum(this->healthMax);
620    this->healthWidget->setValue(this->health);
621  }
622}
623
624
625/**
626 * DEBUG-DRAW OF THE BV-Tree.
627 * @param depth What depth to draw
628 * @param drawMode the mode to draw this entity under
629 */
630void WorldEntity::drawBVTree(int depth, int drawMode) const
631{
632  glMatrixMode(GL_MODELVIEW);
633  glPushMatrix();
634  /* translate */
635  glTranslatef (this->getAbsCoor ().x,
636                this->getAbsCoor ().y,
637                this->getAbsCoor ().z);
638  /* rotate */
639  Vector tmpRot = this->getAbsDir().getSpacialAxis();
640  glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z );
641
642
643  if (this->obbTree)
644    this->obbTree->drawBV(depth, drawMode);
645
646
647  glPopMatrix();
648}
649
650
651/**
652 * Debug the WorldEntity
653 */
654void WorldEntity::debugEntity() const
655{
656  PRINT(0)("WorldEntity %s::%s  (DEBUG)\n", this->getClassName(), this->getName());
657  this->debugNode();
658  PRINT(0)("List: %s ; ModelCount %d - ", ObjectManager::OMListToString(this->objectListNumber) , this->models.size());
659  for (unsigned int i = 0; i < this->models.size(); i++)
660  {
661    if (models[i] != NULL)
662      PRINT(0)(" : %d:%s", i, this->models[i]->getName());
663  }
664  PRINT(0)("\n");
665
666}
667
668
669
670
671/********************************************************************************************
672 NETWORK STUFF
673 ********************************************************************************************/
674
675
676/**
677 * Writes data from network containing information about the state
678 * @param data pointer to data
679 * @param length length of data
680 * @param sender hostID of sender
681 */
682int WorldEntity::writeState( const byte * data, int length, int sender )
683{
684  std::string modelFileName;
685  SYNCHELP_READ_BEGIN();
686
687  SYNCHELP_READ_FKT( PNode::writeState, NWT_WE_PN_WRITESTATE );
688
689  SYNCHELP_READ_STRING( modelFileName, NWT_WE_PN_MODELFILENAME );
690  SYNCHELP_READ_FLOAT( scaling, NWT_WE_PN_SCALING );
691  //check if modelFileName is relative to datadir or absolute
692
693
694  PRINTF(0)("================ LOADING MODEL %s, %f\n", modelFileName.c_str(), scaling);
695
696  if ( modelFileName != "" )
697  {
698    loadModel( modelFileName, scaling);
699    PRINTF(0)("modelfilename: %s\n", getModel( 0 )->getName());
700  }
701
702  /*SYNCHELP_READ_STRINGM( modelFileName );
703
704  if ( strcmp(modelFileName, "") )
705    if ( strstr(modelFileName, ResourceManager::getInstance()->getDataDir()) )
706    {
707      this->md2TextureFileName = new char[strlen(modelFileName)-strlen(ResourceManager::getInstance()->getDataDir())+1];
708      strcpy((char*)this->md2TextureFileName, modelFileName+strlen(ResourceManager::getInstance()->getDataDir()));
709    }
710    else
711    {
712      this->md2TextureFileName = modelFileName;
713    }
714  */
715
716  return SYNCHELP_READ_N;
717}
718
719
720/**
721 * data copied in data will bee sent to another host
722 * @param data pointer to data
723 * @param maxLength max length of data
724 * @return the number of bytes writen
725 */
726int WorldEntity::readState( byte * data, int maxLength )
727{
728  SYNCHELP_WRITE_BEGIN();
729
730  SYNCHELP_WRITE_FKT( PNode::readState, NWT_WE_PN_WRITESTATE );
731
732  if ( getModel(0) && getModel(0)->getName() != "" )
733  {
734    std::string name = getModel( 0 )->getName();
735
736    if (  name.find( ResourceManager::getInstance()->getDataDir() ) == 0 )
737    {
738      name.erase(ResourceManager::getInstance()->getDataDir().size());
739    }
740
741    SYNCHELP_WRITE_STRING( name, NWT_WE_PN_MODELFILENAME );
742  }
743  else
744  {
745    SYNCHELP_WRITE_STRING("", NWT_WE_PN_MODELFILENAME);
746  }
747
748  SYNCHELP_WRITE_FLOAT( scaling, NWT_WE_PN_SCALING );
749  /*if ( this->md2TextureFileName!=NULL && strcmp(this->md2TextureFileName, "") )
750  {
751    SYNCHELP_WRITE_STRING(this->md2TextureFileName);
752  }
753  else
754  {
755    SYNCHELP_WRITE_STRING("");
756  }*/
757
758  return SYNCHELP_WRITE_N;
759}
Note: See TracBrowser for help on using the repository browser.