/* * ORXONOX - the hottest 3D action shooter ever to exist * > www.orxonox.net < * * * License notice: * * 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 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Author: * Felix Schulthess * Co-authors: * Reto Grieder * Oliver Scheuss * Matthias Spalinger * */ #include "HUDNavigation.h" #include #include #include #include #include #include #include "util/Math.h" #include "util/Convert.h" #include "core/CoreIncludes.h" #include "core/XMLPort.h" #include "CameraManager.h" #include "Scene.h" #include "Radar.h" #include "graphics/Camera.h" #include "graphics/Model.h" #include "worldentities/MovableEntity.h" #include "controllers/HumanController.h" #include "worldentities/pawns/Pawn.h" #include "worldentities/WorldEntity.h" #include "core/ConfigValueIncludes.h" #include "tools/TextureGenerator.h" // #include namespace orxonox { bool compareDistance ( std::pair a, std::pair b ) { return a.secondsetConfigValues(); // Set default values this->setFont ( "Monofur" ); this->setTextSize ( 0.05f ); this->setNavMarkerSize ( 0.05f ); this->setDetectionLimit( 10000.0f ); //Ogre::Entity* ent = this->getScene()->getSceneManager()->createEntity("Arrow3D", "Arrow3D.mesh"); Model* arrow = new Model(this); MovableEntity* arrowEntity = new MovableEntity(this); arrow->setMeshSource("Arrow3D.mesh"); arrowEntity->attach(arrow); arrowEntity->setVisible(true); arrowEntity->scale(1); arrowEntity->setPosition(0, 0, -200); arrowEntity->attachToNode(this->overlay3d_); //this->add3DMesh(arrowEntity); this->overlay3d_->setPosition(0, 0, -200); this->overlay3d_->setVisible(false, true); } HUDNavigation::~HUDNavigation() { if ( this->isInitialized() ) { for ( ObjectMap::iterator it = activeObjectList_.begin(); it != activeObjectList_.end(); ) removeObject ( ( it++ )->first ); } sortedObjectList_.clear(); } void HUDNavigation::XMLPort ( Element& xmlelement, XMLPort::Mode mode ) { SUPER ( HUDNavigation, XMLPort, xmlelement, mode ); XMLPortParam ( HUDNavigation, "font", setFont, getFont, xmlelement, mode ); XMLPortParam ( HUDNavigation, "textSize", setTextSize, getTextSize, xmlelement, mode ); XMLPortParam ( HUDNavigation, "navMarkerSize", setNavMarkerSize, getNavMarkerSize, xmlelement, mode ); XMLPortParam ( HUDNavigation, "detectionLimit", setDetectionLimit, getDetectionLimit, xmlelement, mode ); } void HUDNavigation::setFont ( const std::string& font ) { const Ogre::ResourcePtr& fontPtr = Ogre::FontManager::getSingleton().getByName ( font ); if ( fontPtr.isNull() ) { orxout(internal_warning) << "HUDNavigation: Font '" << font << "' not found" << endl; return; } fontName_ = font; for ( ObjectMap::iterator it = activeObjectList_.begin(); it != activeObjectList_.end(); ++it ) { if ( it->second.text_ != NULL ) it->second.text_->setFontName ( fontName_ ); } } const std::string& HUDNavigation::getFont() const { return fontName_; } void HUDNavigation::setTextSize ( float size ) { if ( size <= 0.0f ) { orxout(internal_warning) << "HUDNavigation: Negative font size not allowed" << endl; return; } textSize_ = size; for ( ObjectMap::iterator it = activeObjectList_.begin(); it!=activeObjectList_.end(); ++it ) { if ( it->second.text_ ) it->second.text_->setCharHeight ( size ); } } float HUDNavigation::getTextSize() const { return textSize_; } float HUDNavigation::getArrowSizeX(int dist) { if (dist < 600) dist = 600; return this->getActualSize().x * 900 * navMarkerSize_ / dist; } float HUDNavigation::getArrowSizeY(int dist) { if (dist < 600) dist = 600; return this->getActualSize().y * 900 * navMarkerSize_ / dist; } void HUDNavigation::tick ( float dt ) { SUPER ( HUDNavigation, tick, dt ); Camera* cam = CameraManager::getInstance().getActiveCamera(); if ( cam == NULL ) return; const Matrix4& camTransform = cam->getOgreCamera()->getProjectionMatrix() * cam->getOgreCamera()->getViewMatrix(); for ( sortedList::iterator listIt = sortedObjectList_.begin(); listIt != sortedObjectList_.end(); ++listIt ) { listIt->second = ( int ) ( ( listIt->first->getRVWorldPosition() - HumanController::getLocalControllerSingleton()->getControllableEntity()->getWorldPosition() ).length() + 0.5f ); } sortedObjectList_.sort ( compareDistance ); unsigned int markerCount_ = 0; bool closeEnough_ = false; //only display objects that are close enough to be relevant for the player //for the first element of sortedObjectList_ / the closest waypoint show the Arrow3D if(!sortedObjectList_.empty()) { showArrow3D(); sortedList::iterator firstIt = sortedObjectList_.begin(); Ogre::Vector3 pos1 = firstIt->first->getRVWorldPosition(); Ogre::Vector3 pos2 = cam->getPosition(); Ogre::Quaternion quat = pos2.getRotationTo(pos1); this->overlay3d_->rotate(quat); //this->overlay3d_->setDirection(pos1-pos2); } // for (ObjectMap::iterator it = activeObjectList_.begin(); it != activeObjectList_.end(); ++it) for ( sortedList::iterator listIt = sortedObjectList_.begin(); listIt != sortedObjectList_.end(); ++markerCount_, ++listIt ) { ObjectMap::iterator it = activeObjectList_.find ( listIt->first ); closeEnough_ = listIt->second < detectionLimit_ ; // display radarviewables on HUD if the marker limit and max-distance is not exceeded if ( markerCount_ < markerLimit_ && (closeEnough_ || detectionLimit_ < 0) ) { // Get Distance to HumanController and save it in the TextAreaOverlayElement. int dist = listIt->second; float textLength = 0.0f; //display distance next to cursor if (showDistance){ it->second.text_->setCaption ( multi_cast ( dist ) ); textLength = multi_cast ( dist ).size() * it->second.text_->getCharHeight() * 0.3f; } //display name next to cursor else{ it->second.text_->setCaption(it->first->getRVName()); textLength = it->first->getRVName().size() * it->second.text_->getCharHeight() * 0.3f; } // Transform to screen coordinates Vector3 pos = camTransform * it->first->getRVWorldPosition(); bool outOfView = true; if ( pos.z > 1.0 ) { // z > 1.0 means that the object is behind the camera outOfView = true; // we have to switch all coordinates (if you don't know why, // try linear algebra lectures, because I can't explain..) pos.x = -pos.x; pos.y = -pos.y; } else outOfView = pos.x < -1.0 || pos.x > 1.0 || pos.y < -1.0 || pos.y > 1.0; if ( outOfView ) { // Object is not in view // Change material only if outOfView changed if ( !it->second.wasOutOfView_ ) { it->second.panel_->setMaterialName( TextureGenerator::getMaterialName( "arrows.png", it->first->getRadarObjectColour()) ); it->second.wasOutOfView_ = true; } //float xDistScale = this->getActualSize().x * 1000.0f * navMarkerSize_ / dist; //float yDistScale = this->getActualSize().y * 1000.0f * navMarkerSize_ / dist; // Adjust Arrowsize according to distance it->second.panel_->setDimensions(getArrowSizeX(dist),getArrowSizeY(dist)); // Switch between top, bottom, left and right position of the arrow at the screen border if ( pos.x < pos.y ) { if ( pos.y > -pos.x ) { // Top float position = pos.x / pos.y + 1.0f; it->second.panel_->setPosition ( ( position - it->second.panel_->getWidth() ) * 0.5f, 0.0f ); it->second.panel_->setUV ( 0.5f, 0.0f, 1.0f, 0.5f ); it->second.text_->setLeft ( ( position - textLength ) * 0.5f ); it->second.text_->setTop ( it->second.panel_->getHeight() ); } else { // Left float position = pos.y / pos.x + 1.0f; it->second.panel_->setPosition ( 0.0f, ( position - it->second.panel_->getWidth() ) * 0.5f ); it->second.panel_->setUV ( 0.0f, 0.0f, 0.5f, 0.5f ); it->second.text_->setLeft ( it->second.panel_->getWidth() + 0.01f ); it->second.text_->setTop ( ( position - it->second.text_->getCharHeight() ) * 0.5f ); } } else { if ( pos.y < -pos.x ) { // Bottom float position = -pos.x / pos.y + 1.0f; it->second.panel_->setPosition ( ( position - it->second.panel_->getWidth() ) * 0.5f, 1.0f - it->second.panel_->getHeight() ); it->second.panel_->setUV ( 0.0f, 0.5f, 0.5f, 1.0f ); it->second.text_->setLeft ( ( position - textLength ) * 0.5f ); it->second.text_->setTop ( 1.0f - it->second.panel_->getHeight() - it->second.text_->getCharHeight() ); } else { // Right float position = -pos.y / pos.x + 1.0f; it->second.panel_->setPosition ( 1.0f - it->second.panel_->getWidth(), ( position - it->second.panel_->getHeight() ) * 0.5f ); it->second.panel_->setUV ( 0.5f, 0.5f, 1.0f, 1.0f ); it->second.text_->setLeft ( 1.0f - it->second.panel_->getWidth() - textLength - 0.01f ); it->second.text_->setTop ( ( position - it->second.text_->getCharHeight() ) * 0.5f ); } } } else { // Object is in view // Change material only if outOfView changed if ( it->second.wasOutOfView_ ) { //it->second.panel_->setMaterialName ( "Orxonox/NavTDC" ); it->second.panel_->setMaterialName( TextureGenerator::getMaterialName( "tdc.png", it->first->getRadarObjectColour()) ); it->second.panel_->setDimensions ( navMarkerSize_ * this->getActualSize().x, navMarkerSize_ * this->getActualSize().y ); it->second.wasOutOfView_ = false; } // Position marker it->second.panel_->setUV ( 0.0f, 0.0f, 1.0f, 1.0f ); it->second.panel_->setLeft ( ( pos.x + 1.0f - it->second.panel_->getWidth() ) * 0.5f ); it->second.panel_->setTop ( ( -pos.y + 1.0f - it->second.panel_->getHeight() ) * 0.5f ); // Position text it->second.text_->setLeft ( ( pos.x + 1.0f + it->second.panel_->getWidth() ) * 0.5f ); it->second.text_->setTop ( ( -pos.y + 1.0f + it->second.panel_->getHeight() ) * 0.5f ); } // Make sure the overlays are shown it->second.panel_->show(); it->second.text_->show(); } else // do not display on HUD { it->second.panel_->hide(); it->second.text_->hide(); } } } /** Overridden method of OrxonoxOverlay. @details Usually the entire overlay scales with scale(). Here we obviously have to adjust this. */ void HUDNavigation::sizeChanged() { // Use size to compensate for aspect ratio if enabled. float xScale = this->getActualSize().x; float yScale = this->getActualSize().y; for ( ObjectMap::iterator it = activeObjectList_.begin(); it!=activeObjectList_.end(); ++it ) { if ( it->second.panel_ != NULL ) it->second.panel_->setDimensions ( navMarkerSize_ * xScale, navMarkerSize_ * yScale ); if ( it->second.text_ != NULL ) it->second.text_->setCharHeight ( it->second.text_->getCharHeight() * yScale ); } } void HUDNavigation::addObject ( RadarViewable* object ) { if( showObject(object)==false ) return; if ( activeObjectList_.size() >= markerLimit_ ) if ( object == NULL ) return; // Object hasn't been added yet (we know that) assert ( this->activeObjectList_.find ( object ) == this->activeObjectList_.end() ); // Scales used for dimensions and text size float xScale = this->getActualSize().x; float yScale = this->getActualSize().y; // Create everything needed to display the object on the radar and add it to the map // Create arrow/marker Ogre::PanelOverlayElement* panel = static_cast ( Ogre::OverlayManager::getSingleton() .createOverlayElement ( "Panel", "HUDNavigation_navMarker_" + getUniqueNumberString() ) ); // panel->setMaterialName ( "Orxonox/NavTDC" ); panel->setMaterialName( TextureGenerator::getMaterialName( "tdc.png", object->getRadarObjectColour()) ); panel->setDimensions ( navMarkerSize_ * xScale, navMarkerSize_ * yScale ); // panel->setColour( object->getRadarObjectColour() ); Ogre::TextAreaOverlayElement* text = static_cast ( Ogre::OverlayManager::getSingleton() .createOverlayElement ( "TextArea", "HUDNavigation_navText_" + getUniqueNumberString() ) ); text->setFontName ( this->fontName_ ); text->setCharHeight ( text->getCharHeight() * yScale ); text->setColour( object->getRadarObjectColour() ); panel->hide(); text->hide(); ObjectInfo tempStruct = {panel, text, false /*, TODO: initialize wasOutOfView_ */}; activeObjectList_[object] = tempStruct; this->background_->addChild ( panel ); this->background_->addChild ( text ); sortedObjectList_.push_front ( std::make_pair ( object, ( unsigned int ) 0 ) ); } void HUDNavigation::removeObject ( RadarViewable* viewable ) { ObjectMap::iterator it = activeObjectList_.find ( viewable ); if ( activeObjectList_.find ( viewable ) != activeObjectList_.end() ) { // Detach overlays this->background_->removeChild ( it->second.panel_->getName() ); this->background_->removeChild ( it->second.text_->getName() ); // Properly destroy the overlay elements (do not use delete!) Ogre::OverlayManager::getSingleton().destroyOverlayElement ( it->second.panel_ ); Ogre::OverlayManager::getSingleton().destroyOverlayElement ( it->second.text_ ); // Remove from the list activeObjectList_.erase ( viewable ); } for ( sortedList::iterator listIt = sortedObjectList_.begin(); listIt != sortedObjectList_.end(); ++listIt ) { if ( (listIt->first) == viewable ) { sortedObjectList_.erase ( listIt ); break; } } } void HUDNavigation::objectChanged(RadarViewable* viewable) { // TODO: niceification neccessary ;) removeObject(viewable); addObject(viewable); } bool HUDNavigation::showObject(RadarViewable* rv) { if ( rv == dynamic_cast ( this->getOwner() ) ) return false; assert( rv->getWorldEntity() ); if ( rv->getWorldEntity()->isVisible()==false || rv->getRadarVisibility()==false ) return false; return true; } void HUDNavigation::changedOwner() { const std::set& respawnObjects = this->getOwner()->getScene()->getRadar()->getRadarObjects(); for ( std::set::const_iterator it = respawnObjects.begin(); it != respawnObjects.end(); ++it ) { if ( ! ( *it )->isHumanShip_ ) this->addObject ( *it ); } } void HUDNavigation::showArrow3D() { this->overlay3d_->setVisible(true, true);} void HUDNavigation::hideArrow3D() { this->overlay3d_->setVisible(false, true);} }