/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli co-programmer: Christian Meyer */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WORLD_ENTITY #include "world_entity.h" #include "shell_command.h" #include "model.h" #include "md2Model.h" #include "resource_manager.h" #include "load_param.h" #include "vector.h" #include "obb_tree.h" #include "glgui_bar.h" #include "state.h" using namespace std; SHELL_COMMAND(model, WorldEntity, loadModel) ->describe("sets the Model of the WorldEntity") ->defaultValues(2, "models/ships/fighter.obj", 1.0); SHELL_COMMAND(debugEntity, WorldEntity, debugWE); /** * Loads the WordEntity-specific Part of any derived Class * * @param root: Normally NULL, as the Derived Entities define a loadParams Function themeselves, * that can calls WorldEntities loadParams for itself. */ WorldEntity::WorldEntity() : Synchronizeable() { this->setClassID(CL_WORLD_ENTITY, "WorldEntity"); this->obbTree = NULL; this->energyWidget = NULL; this->energyMax = 1.0f; this->energy = 1.0f; this->md2TextureFileName = NULL; this->setVisibiliy(true); this->objectListNumber = OM_INIT; this->objectListIterator = NULL; this->toList(OM_NULL); } /** * standard destructor */ WorldEntity::~WorldEntity () { // Delete the obbTree if( this->obbTree != NULL) delete this->obbTree; if (this->energyWidget != NULL) delete this->energyWidget; // Delete the model (unregister it with the ResourceManager) for (unsigned int i = 0; i < this->models.size(); i++) this->setModel(NULL, i); State::getObjectManager()->toList(this, OM_INIT); } /** * loads the WorldEntity Specific Parameters. * @param root: the XML-Element to load the Data From */ void WorldEntity::loadParams(const TiXmlElement* root) { // Do the PNode loading stuff PNode::loadParams(root); LoadParam(root, "md2texture", this, WorldEntity, loadMD2Texture) .describe("the fileName of the texture, that should be loaded onto this world-entity. (must be relative to the data-dir)") .defaultValues(1, NULL); // Model Loading LoadParam(root, "model", this, WorldEntity, loadModel) .describe("the fileName of the model, that should be loaded onto this world-entity. (must be relative to the data-dir)") .defaultValues(3, NULL, 1.0f, 0); LoadParam(root, "maxEnergy", this, WorldEntity, setMaxEnergy) .describe("The Maximum energy that can be loaded onto this entity") .defaultValues(1, 1.0f); LoadParam(root, "energy", this, WorldEntity, setEnergy) .describe("The Energy the WorldEntity has at this moment") .defaultValues(1, 1.0f); } /** * loads a Model onto a WorldEntity * @param fileName the name of the model to load * @param scaling the Scaling of the model * * @todo fix this, so it only has one loadModel-Function. */ void WorldEntity::loadModel(const char* fileName, float scaling, unsigned int modelNumber) { this->scaling = scaling; if ( fileName != NULL && strcmp(fileName, "") ) { // search for the special character # in the LoadParam if (strchr(fileName, '#') != NULL) { PRINTF(4)("Found # in %s... searching for LOD's\n", fileName); char* lodFile = new char[strlen(fileName)+1]; strcpy(lodFile, fileName); char* depth = strchr(lodFile, '#'); for (unsigned int i = 0; i < 5; i++) { *depth = 48+(int)i; printf("-------%s\n", lodFile); if (ResourceManager::isInDataDir(lodFile)) this->loadModel(lodFile, scaling, i); } return; } if (scaling == 0.0) { scaling = 1.0; PRINTF(1)("YOU GAVE ME A CRAPY SCALE resetting to 1\n"); } if(strstr(fileName, ".obj")) { PRINTF(4)("fetching OBJ file: %s\n", fileName); if (scaling == 1.0) this->setModel((Model*)ResourceManager::getInstance()->load(fileName, OBJ, RP_CAMPAIGN), modelNumber); else this->setModel((Model*)ResourceManager::getInstance()->load(fileName, OBJ, RP_CAMPAIGN, &scaling), modelNumber); if( modelNumber == 0) this->buildObbTree(4); } else if(strstr(fileName, ".md2")) { PRINTF(4)("fetching MD2 file: %s\n", fileName); Model* m = new MD2Model(fileName, this->md2TextureFileName); //this->setModel((Model*)ResourceManager::getInstance()->load(fileName, MD2, RP_CAMPAIGN), 0); this->setModel(m, 0); } } else { this->setModel(NULL); } } /** * sets a specific Model for the Object. * @param model The Model to set * @param modelNumber the n'th model in the List to get. */ void WorldEntity::setModel(Model* model, unsigned int modelNumber) { if (this->models.size() <= modelNumber) this->models.resize(modelNumber+1, NULL); if (this->models[modelNumber] != NULL) { Resource* resource = ResourceManager::getInstance()->locateResourceByPointer(this->models[modelNumber]); // if (resource != NULL) ResourceManager::getInstance()->unload(resource, RP_LEVEL); } else delete this->models[modelNumber]; this->models[modelNumber] = model; // if (this->model != NULL) // this->buildObbTree(4); } /** * builds the obb-tree * @param depth the depth to calculate */ bool WorldEntity::buildObbTree(int depth) { if (this->obbTree) delete this->obbTree; if (this->models[0] != NULL) { PRINTF(4)("creating obb tree\n"); this->obbTree = new OBBTree(depth, this->models[0]->getModelInfo()); return true; } else { PRINTF(2)("could not create obb-tree, because no model was loaded yet\n"); this->obbTree = NULL; return false; } } /** * @brief moves this entity to the List OM_List * @param list the list to set this Entity to. * * this is the same as a call to State::getObjectManager()->toList(entity , list); * directly, but with an easier interface. * * @todo inline this (peut etre) */ void WorldEntity::toList(OM_LIST list) { State::getObjectManager()->toList(this, list); } /** * sets the character attributes of a worldentity * @param character attributes * * these attributes don't have to be set, only use them, if you need them */ //void WorldEntity::setCharacterAttributes(CharacterAttributes* charAttr) //{} /** * this function is called, when two entities collide * @param entity: the world entity with whom it collides * * Implement behaviour like damage application or other miscellaneous collision stuff in this function */ void WorldEntity::collidesWith(WorldEntity* entity, const Vector& location) { /** * THIS IS A DEFAULT COLLISION-Effect. * IF YOU WANT TO CREATE A SPECIFIC COLLISION ON EACH OBJECT * USE:: * if (entity->isA(CL_WHAT_YOU_ARE_LOOKING_FOR)) { printf "dothings"; }; * * You can always define a default Action.... don't be affraid just test it :) */ // PRINTF(3)("collision %s vs %s @ (%f,%f,%f)\n", this->getClassName(), entity->getClassName(), location.x, location.y, location.z); } /** * this is called immediately after the Entity has been constructed, initialized and then Spawned into the World * */ void WorldEntity::postSpawn () {} /** * this method is called by the world if the WorldEntity leaves valid gamespace * * For free entities this means it left the Track boundaries. With bound entities it means its Location adresses a * place that is not in the world anymore. In both cases you might have to take extreme measures (a.k.a. call destroy). * * NOT YET IMPLEMENTED */ void WorldEntity::leftWorld () {} /** * this method is called every frame * @param time: the time in seconds that has passed since the last tick * * Handle all stuff that should update with time inside this method (movement, animation, etc.) */ void WorldEntity::tick(float time) {} /** * the entity is drawn onto the screen with this function * * This is a central function of an entity: call it to let the entity painted to the screen. * Just override this function with whatever you want to be drawn. */ void WorldEntity::draw() const { //PRINTF(0)("(%s::%s)\n", this->getClassName(), this->getName()); // assert(!unlikely(this->models.empty())); { glMatrixMode(GL_MODELVIEW); glPushMatrix(); /* translate */ glTranslatef (this->getAbsCoor ().x, this->getAbsCoor ().y, this->getAbsCoor ().z); Vector tmpRot = this->getAbsDir().getSpacialAxis(); glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z ); // This Draws the LOD's float cameraDistance = (State::getCamera()->getAbsCoor() - this->getAbsCoor()).len(); if (cameraDistance > 30 && this->models.size() >= 3 && this->models[2] != NULL) { this->models[2]->draw(); } else if (cameraDistance > 10 && this->models.size() >= 2 && this->models[1] != NULL) { this->models[1]->draw(); } else if (this->models.size() >= 1 && this->models[0] != NULL) { this->models[0]->draw(); } glPopMatrix(); } } /** * @param energy the Energy to add. * @returns the energy left (this->energyMax - energy+this->energy) */ float WorldEntity::addEnergy(float energy) { this->energy += energy; if (this->energy > this->energyMax) { float retEnergy = this->energyMax - this->energy; this->energy = this->energyMax; this->updateEnergyWidget(); return retEnergy; } this->updateEnergyWidget(); return 0.0; } /** * @param energy the Energy to be removed * @returns 0.0 or the rest, that was not substracted (bellow 0.0) */ float WorldEntity::removeEnergy(float energy) { this->energy -= energy; if (this->energy < 0) { float retEnergy = -this->energy; this->energy = 0.0f; this->updateEnergyWidget(); return retEnergy; } this->updateEnergyWidget(); return 0.0; } /** * @param maxEnergy the maximal energy that can be loaded onto the entity. */ void WorldEntity::setMaxEnergy(float maxEnergy) { this->energyMax = maxEnergy; if (this->energy > this->energyMax) { PRINTF(3)("new maxEnergy is bigger as the old energy. Did you really intend to do this for (%s::%s)\n", this->getClassName(), this->getName()); this->energy = this->energyMax; } this->updateEnergyWidget(); } /** * @brief creates the EnergyWidget * * since not all entities need an EnergyWidget, it is only created on request. */ void WorldEntity::createEnergyWidget() { if (this->energyWidget == NULL) { this->energyWidget = new GLGuiBar(); this->energyWidget->setSize2D(30,400); this->energyWidget->setAbsCoor2D(10,100); this->updateEnergyWidget(); } else PRINTF(3)("Allready created the EnergyWidget for %s::%s\n", this->getClassName(), this->getName()); } GLGuiWidget* WorldEntity::getEnergyWidget() { this->createEnergyWidget(); return this->energyWidget; } /** * @param visibility shows or hides the energy-bar * (creates the widget if needed) */ void WorldEntity::setEnergyWidgetVisibilit(bool visibility) { if (visibility) { if (this->energyWidget != NULL) this->energyWidget->show(); else { this->createEnergyWidget(); this->updateEnergyWidget(); this->energyWidget->show(); } } else if (this->energyWidget != NULL) this->energyWidget->hide(); } /** * @brief updates the EnergyWidget */ void WorldEntity::updateEnergyWidget() { if (this->energyWidget != NULL) { this->energyWidget->setMaximum(this->energyMax); this->energyWidget->setValue(this->energy); } } /** * DEBUG-DRAW OF THE BV-Tree. * @param depth What depth to draw * @param drawMode the mode to draw this entity under */ void WorldEntity::drawBVTree(unsigned int depth, int drawMode) const { glMatrixMode(GL_MODELVIEW); glPushMatrix(); /* translate */ glTranslatef (this->getAbsCoor ().x, this->getAbsCoor ().y, this->getAbsCoor ().z); /* rotate */ Vector tmpRot = this->getAbsDir().getSpacialAxis(); glRotatef (this->getAbsDir().getSpacialAxisAngle(), tmpRot.x, tmpRot.y, tmpRot.z ); if (this->obbTree) this->obbTree->drawBV(depth, drawMode); glPopMatrix(); } /** * Debug the WorldEntity */ void WorldEntity::debugEntity() const { PRINT(0)("WorldEntity %s::%s (DEBUG)\n", this->getClassName(), this->getName()); this->debugNode(); PRINT(0)("List: %s ; ModelCount %d - ", ObjectManager::OMListToString(this->objectListNumber) , this->models.size()); for (unsigned int i = 0; i < this->models.size(); i++) { if (models[i] != NULL) PRINT(0)(" : %d:%s", i, this->models[i]->getName()); } PRINT(0)("\n"); } /******************************************************************************************** NETWORK STUFF ********************************************************************************************/ /** * Writes data from network containing information about the state * @param data pointer to data * @param length length of data * @param sender hostID of sender */ int WorldEntity::writeState( const byte * data, int length, int sender ) { char* modelFileName; SYNCHELP_READ_BEGIN(); SYNCHELP_READ_FKT( PNode::writeState ); SYNCHELP_READ_STRINGM( modelFileName ); SYNCHELP_READ_FLOAT( scaling ); //check if modelFileName is relative to datadir or absolute if ( strcmp(modelFileName, "") ) { loadModel( modelFileName, scaling ); } delete[] modelFileName; /*SYNCHELP_READ_STRINGM( modelFileName ); if ( strcmp(modelFileName, "") ) if ( strstr(modelFileName, ResourceManager::getInstance()->getDataDir()) ) { this->md2TextureFileName = new char[strlen(modelFileName)-strlen(ResourceManager::getInstance()->getDataDir())+1]; strcpy((char*)this->md2TextureFileName, modelFileName+strlen(ResourceManager::getInstance()->getDataDir())); } else { this->md2TextureFileName = modelFileName; } */ return SYNCHELP_READ_N; } /** * data copied in data will bee sent to another host * @param data pointer to data * @param maxLength max length of data * @return the number of bytes writen */ int WorldEntity::readState( byte * data, int maxLength ) { SYNCHELP_WRITE_BEGIN(); SYNCHELP_WRITE_FKT( PNode::readState ); if ( getModel(0) && getModel(0)->getName() ) { char* name = (char*)(getModel( 0 )->getName()); if ( strstr(name, ResourceManager::getInstance()->getDataDir()) ) { name += strlen(ResourceManager::getInstance()->getDataDir()); } SYNCHELP_WRITE_STRING( name ); } else { SYNCHELP_WRITE_STRING(""); } SYNCHELP_WRITE_FLOAT( scaling ); /*if ( this->md2TextureFileName!=NULL && strcmp(this->md2TextureFileName, "") ) { SYNCHELP_WRITE_STRING(this->md2TextureFileName); } else { SYNCHELP_WRITE_STRING(""); }*/ return SYNCHELP_WRITE_N; }