/* 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 "list.h" #include "vector.h" #include "obb_tree.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); /** * 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(const TiXmlElement* root) : Synchronizeable() { this->setClassID(CL_WORLD_ENTITY, "WorldEntity"); this->obbTree = NULL; if (root != NULL) this->loadParams(root); 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; // 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 static_cast(this)->loadParams(root); // 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); } /** * 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) { if (fileName != NULL) { // 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(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); // MD2Model* m = (MD2Model*)ResourceManager::getInstance()->load(fileName, MD2, RP_CAMPAIGN); MD2Model* m = new MD2Model(fileName, ""); m->debug(); //this->setModel((Model*)ResourceManager::getInstance()->load(fileName, MD2, RP_CAMPAIGN), 0); this->setModel((Model*)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(unsigned int depth) { if (this->obbTree) delete this->obbTree; if (this->models[0] != NULL) { PRINTF(4)("creating obb tree\n"); this->obbTree = new OBBTree(depth, (sVec3D*)this->models[0]->getVertexArray(), this->models[0]->getVertexCount()); 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 { this->drawLODsafe(); } /** * this functions draws the model automaticaly in multiple LOD */ void WorldEntity::drawLODsafe() const { if (!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(); } } /** * 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(); }