Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/spaceNavigation/src/modules/overlays/hud/HUDNavigation.cc @ 9486

Last change on this file since 9486 was 9486, checked in by mottetb, 11 years ago

hallo

  • Property svn:eol-style set to native
File size: 24.3 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Felix Schulthess
24 *   Co-authors:
25 *      Reto Grieder
26 *      Oliver Scheuss
27 *      Matthias Spalinger
28 *
29 */
30
31#include "HUDNavigation.h"
32
33#include <OgreCamera.h>
34#include <OgreFontManager.h>
35#include <OgreOverlayManager.h>
36#include <OgreTextAreaOverlayElement.h>
37#include <OgrePanelOverlayElement.h>
38
39#include <typeinfo>
40
41#include "util/Math.h"
42#include "util/Convert.h"
43#include "core/command/ConsoleCommand.h"
44#include "core/CoreIncludes.h"
45#include "core/XMLPort.h"
46#include "CameraManager.h"
47#include "Scene.h"
48#include "Radar.h"
49#include "graphics/Camera.h"
50#include "controllers/HumanController.h"
51#include "worldentities/pawns/Pawn.h"
52#include "worldentities/WorldEntity.h"
53#include "core/ConfigValueIncludes.h"
54#include "tools/TextureGenerator.h"
55// #include <boost/bind/bind_template.hpp>
56
57
58namespace orxonox
59{
60
61    SetConsoleCommand("HUDNavigation","selectClosest", &HUDNavigation::selectClosestTarget).addShortcut().keybindMode(KeybindMode::OnPress);
62    SetConsoleCommand("HUDNavigation","selectNext", &HUDNavigation::selectNextTarget).addShortcut().keybindMode(KeybindMode::OnPress);
63
64    static bool compareDistance(std::pair<RadarViewable*, unsigned int> a,
65            std::pair<RadarViewable*, unsigned int> b)
66    {
67        return a.second < b.second;
68    }
69    CreateFactory ( HUDNavigation );
70
71    HUDNavigation* HUDNavigation::localHUD_s = 0;
72
73    HUDNavigation::HUDNavigation(BaseObject* creator) :
74        OrxonoxOverlay(creator)
75    {
76        RegisterObject(HUDNavigation)
77;        this->setConfigValues();
78
79        // Set default values
80        this->setFont("Monofur");
81        this->setTextSize(0.05f);
82        this->setNavMarkerSize(0.03f);
83        this->setAimMarkerSize(0.02f);
84
85        this->setDetectionLimit(10000.0f);
86        this->currentMunitionSpeed_ = 2500.0f;
87
88        this->closestTarget_ = true;
89        this->nextTarget_ = false;
90        HUDNavigation::localHUD_s = this;
91    }
92
93    HUDNavigation::~HUDNavigation()
94    {
95        if (this->isInitialized())
96        {
97            for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it != this->activeObjectList_.end();)
98            removeObject((it++)->first);
99        }
100        this->sortedObjectList_.clear();
101    }
102
103    void HUDNavigation::setConfigValues()
104    {
105        SetConfigValue(markerLimit_, 3);
106        SetConfigValue(showDistance_, false);
107    }
108
109    void HUDNavigation::XMLPort(Element& xmlelement, XMLPort::Mode mode)
110    {
111        SUPER(HUDNavigation, XMLPort, xmlelement, mode);
112
113        XMLPortParam(HUDNavigation, "font", setFont, getFont, xmlelement, mode);
114        XMLPortParam(HUDNavigation, "textSize", setTextSize, getTextSize, xmlelement, mode);
115        XMLPortParam(HUDNavigation, "navMarkerSize", setNavMarkerSize, getNavMarkerSize, xmlelement, mode);
116        XMLPortParam(HUDNavigation, "detectionLimit", setDetectionLimit, getDetectionLimit, xmlelement, mode);
117        XMLPortParam(HUDNavigation, "aimMarkerSize", setAimMarkerSize, getAimMarkerSize, xmlelement, mode);
118    }
119
120    void HUDNavigation::setFont(const std::string& font)
121    {
122        const Ogre::ResourcePtr& fontPtr = Ogre::FontManager::getSingleton().getByName(font);
123        if (fontPtr.isNull())
124        {
125            orxout(internal_warning) << "HUDNavigation: Font '" << font << "' not found" << endl;
126            return;
127        }
128        this->fontName_ = font;
129        for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it != this->activeObjectList_.end(); ++it)
130        {
131            if (it->second.text_ != NULL)
132            it->second.text_->setFontName(this->fontName_);
133        }
134    }
135
136    const std::string& HUDNavigation::getFont() const
137    {
138        return this->fontName_;
139    }
140
141    void HUDNavigation::setTextSize(float size)
142    {
143        if (size <= 0.0f)
144        {
145            orxout(internal_warning) << "HUDNavigation: Negative font size not allowed" << endl;
146            return;
147        }
148        this->textSize_ = size;
149        for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it!=this->activeObjectList_.end(); ++it)
150        {
151            if (it->second.text_)
152            it->second.text_->setCharHeight(size);
153        }
154    }
155
156    float HUDNavigation::getTextSize() const
157    {
158        return this->textSize_;
159    }
160
161    float HUDNavigation::getArrowSizeX(int dist) const
162    {
163        if (dist < 600)
164        dist = 600;
165        return this->getActualSize().x * 900 * this->navMarkerSize_ / dist;
166    }
167
168    float HUDNavigation::getArrowSizeY(int dist) const
169    {
170        if (dist < 600)
171        dist = 600;
172        return this->getActualSize().y * 900 * this->navMarkerSize_ / dist;
173    }
174
175    void HUDNavigation::tick(float dt)
176    {
177        SUPER(HUDNavigation, tick, dt);
178
179        Camera* cam = CameraManager::getInstance().getActiveCamera();
180        if (cam == NULL)
181        return;
182        const Matrix4& camTransform = cam->getOgreCamera()->getProjectionMatrix() * cam->getOgreCamera()->getViewMatrix();
183
184        for (std::list<std::pair<RadarViewable*, unsigned int> >::iterator listIt = this->sortedObjectList_.begin(); listIt != this->sortedObjectList_.end(); ++listIt)
185        listIt->second = (int)((listIt->first->getRVWorldPosition() - HumanController::getLocalControllerSingleton()->getControllableEntity()->getWorldPosition()).length() + 0.5f);
186
187        this->sortedObjectList_.sort(compareDistance);
188
189        unsigned int markerCount = 0;
190        bool closeEnough = false; // only display objects that are close enough to be relevant for the player
191
192        bool nextHasToBeSelected = false;
193
194        for (std::list<std::pair<RadarViewable*, unsigned int> >::iterator listIt = this->sortedObjectList_.begin(); listIt != this->sortedObjectList_.end(); ++markerCount, ++listIt)
195        {
196            std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.find(listIt->first);
197            closeEnough = listIt->second < this->detectionLimit_;
198            // display radarviewables on HUD if the marker limit and max-distance is not exceeded
199            if (markerCount < this->markerLimit_ && (closeEnough || this->detectionLimit_ < 0))
200            {
201                // Get Distance to HumanController and save it in the TextAreaOverlayElement.
202                int dist = listIt->second;
203                float textLength = 0.0f;
204
205                if (this->showDistance_)
206                {
207                    //display distance next to cursor
208                    it->second.text_->setCaption(multi_cast<std::string>(dist));
209                    textLength = multi_cast<std::string>(dist).size() * it->second.text_->getCharHeight() * 0.3f;
210                }
211                else
212                {
213                    //display name next to cursor
214                    it->second.text_->setCaption(it->first->getRadarName());
215                    textLength = it->first->getRadarName().size() * it->second.text_->getCharHeight() * 0.3f;
216                }
217
218                // select the object that aim-assistant indicates
219                if(this->closestTarget_)
220                // select the closest object
221                {
222                    if(listIt == this->sortedObjectList_.begin())
223                    {
224                        it->second.selected_ = true;
225                    }
226                    else if(it->second.selected_)
227                    {
228                        it->second.selected_ = false;
229                    }
230
231                }
232                else if(this->nextTarget_)
233                // select the next object in sortedObjectList
234                {
235                    if(nextHasToBeSelected){
236                        it->second.selected_ = true;
237                        nextHasToBeSelected = false;
238                    }
239                    else if(it->second.selected_)
240                    {
241                        nextHasToBeSelected = true;
242                        it->second.selected_ = false;
243
244                        // check if there's a next object
245                        listIt++;
246                        if(listIt != this->sortedObjectList_.end())
247                        {
248                            // and if the marker limit and max-distance are not exceeded for it
249                            if (markerCount + 1 >= this->markerLimit_ ||
250                                    (listIt->second > this->detectionLimit_ && detectionLimit_ >= 0))
251                            {
252                                // otherwise select the closest object
253                                this->activeObjectList_.find(this->sortedObjectList_.begin()->first)->second.selected_ = true;
254                                nextHasToBeSelected = false;
255                            }
256                        }
257                        listIt--;
258                    }
259                }
260
261
262                // Transform to screen coordinates
263                Vector3 pos = camTransform * it->first->getRVWorldPosition();
264
265                bool outOfView = true;
266                if (pos.z > 1.0)
267                {
268                    // z > 1.0 means that the object is behind the camera
269                    outOfView = true;
270                    // we have to switch all coordinates (if you don't know why,
271                    // try linear algebra lectures, because I can't explain..)
272                    pos.x = -pos.x;
273                    pos.y = -pos.y;
274                }
275                else
276                    outOfView = pos.x < -1.0 || pos.x > 1.0 || pos.y < -1.0 || pos.y > 1.0;
277
278                if (outOfView)
279                {
280                    // Object is not in view
281
282                    // Change material only if outOfView changed
283                    if (!it->second.wasOutOfView_)
284                    {
285                        it->second.panel_->setMaterialName(TextureGenerator::getMaterialName("arrows.png", it->first->getRadarObjectColour()));
286                        it->second.wasOutOfView_ = true;
287                        it->second.target_->hide();
288                    }
289
290                    //float xDistScale = this->getActualSize().x * 1000.0f * this->navMarkerSize_ / dist;
291                    //float yDistScale = this->getActualSize().y * 1000.0f * this->navMarkerSize_ / dist;
292
293                    // Adjust Arrowsize according to distance
294                    it->second.panel_->setDimensions(getArrowSizeX(dist), getArrowSizeY(dist));
295
296                    // Switch between top, bottom, left and right position of the arrow at the screen border
297                    if (pos.x < pos.y)
298                    {
299                        if (pos.y > -pos.x)
300                        {
301                            // Top
302                            float position = pos.x / pos.y + 1.0f;
303                            it->second.panel_->setPosition((position - it->second.panel_->getWidth()) * 0.5f, 0.0f);
304                            it->second.panel_->setUV(0.5f, 0.0f, 1.0f, 0.5f);
305                            it->second.text_->setLeft((position - textLength) * 0.5f);
306                            it->second.text_->setTop(it->second.panel_->getHeight());
307                        }
308                        else
309                        {
310                            // Left
311                            float position = pos.y / pos.x + 1.0f;
312                            it->second.panel_->setPosition(0.0f, (position - it->second.panel_->getWidth()) * 0.5f);
313                            it->second.panel_->setUV(0.0f, 0.0f, 0.5f, 0.5f);
314                            it->second.text_->setLeft(it->second.panel_->getWidth() + 0.01f);
315                            it->second.text_->setTop((position - it->second.text_->getCharHeight()) * 0.5f);
316                        }
317                    }
318                    else
319                    {
320                        if (pos.y < -pos.x)
321                        {
322                            // Bottom
323                            float position = -pos.x / pos.y + 1.0f;
324                            it->second.panel_->setPosition((position - it->second.panel_->getWidth()) * 0.5f, 1.0f - it->second.panel_->getHeight());
325                            it->second.panel_->setUV(0.0f, 0.5f, 0.5f, 1.0f );
326                            it->second.text_->setLeft((position - textLength) * 0.5f);
327                            it->second.text_->setTop(1.0f - it->second.panel_->getHeight() - it->second.text_->getCharHeight());
328                        }
329                        else
330                        {
331                            // Right
332                            float position = -pos.y / pos.x + 1.0f;
333                            it->second.panel_->setPosition(1.0f - it->second.panel_->getWidth(), (position - it->second.panel_->getHeight()) * 0.5f);
334                            it->second.panel_->setUV(0.5f, 0.5f, 1.0f, 1.0f);
335                            it->second.text_->setLeft(1.0f - it->second.panel_->getWidth() - textLength - 0.01f);
336                            it->second.text_->setTop((position - it->second.text_->getCharHeight()) * 0.5f);
337                        }
338                    }
339                }
340                else
341                {
342                    // Object is in view
343
344                    // Change material only if outOfView changed
345                    if (it->second.wasOutOfView_)
346                    {
347                        //it->second.panel_->setMaterialName("Orxonox/NavTDC");
348                        it->second.panel_->setMaterialName(TextureGenerator::getMaterialName("tdc.png", it->first->getRadarObjectColour()));
349                        it->second.panel_->setDimensions(this->navMarkerSize_ * this->getActualSize().x, this->navMarkerSize_ * this->getActualSize().y);
350                        it->second.target_->setDimensions(this->aimMarkerSize_ * this->getActualSize().x, this->aimMarkerSize_ * this->getActualSize().y);
351                        it->second.wasOutOfView_ = false;
352                    }
353
354                    // Position marker
355                    it->second.panel_->setUV(0.0f, 0.0f, 1.0f, 1.0f);
356                    it->second.panel_->setLeft((pos.x + 1.0f - it->second.panel_->getWidth()) * 0.5f);
357                    it->second.panel_->setTop((-pos.y + 1.0f - it->second.panel_->getHeight()) * 0.5f);
358
359                    // Position text
360                    it->second.text_->setLeft((pos.x + 1.0f + it->second.panel_->getWidth()) * 0.5f);
361                    it->second.text_->setTop((-pos.y + 1.0f + it->second.panel_->getHeight()) * 0.5f);
362
363                    // Make sure the overlays are shown
364                    it->second.panel_->show();
365                    it->second.text_->show();
366
367                    // Target marker
368                    const Pawn* pawn = dynamic_cast<const Pawn*>(it->first->getWorldEntity());
369                    /* Pawn* humanPawn = HumanController::getLocalControllerEntityAsPawn();*/
370                    if(!it->second.selected_
371                            || it->first->getRVVelocity().squaredLength() == 0
372                            || pawn == NULL
373                            /* TODO : improve getTeam in such a way that it works
374                             * || humanPawn == NULL
375                             * || pawn->getTeam() == humanPawn->getTeam()*/)
376                    {
377                        // don't show marker for not selected enemies nor if the selected doesn't move
378                        it->second.target_->hide();
379                    }
380                    else // object is selected and moves
381                    {
382                        // get the aim position
383                        Vector3* targetPos = this->toAimPosition(it->first);
384                        // Transform to screen coordinates
385                        Vector3 screenPos = camTransform * (*targetPos);
386                        // Check if the target marker is in view too
387                        if(screenPos.z > 1 || screenPos.x < -1.0 || screenPos.x > 1.0
388                                || screenPos.y < -1.0 || screenPos.y > 1.0)
389                        {
390                            it->second.target_->hide();
391                        }
392                        else
393                        {
394                            it->second.target_->setLeft((screenPos.x + 1.0f - it->second.target_->getWidth()) * 0.5f);
395                            it->second.target_->setTop((-screenPos.y + 1.0f - it->second.target_->getHeight()) * 0.5f);
396                            it->second.target_->show();
397                        }
398                        delete targetPos;
399                    }
400
401                }
402            }
403            else // do not display on HUD
404
405            {
406                it->second.panel_->hide();
407                it->second.text_->hide();
408                it->second.target_->hide();
409            }
410        }
411
412        this->closestTarget_ = false;
413        this->nextTarget_ = false;
414    }
415
416    /** Overridden method of OrxonoxOverlay.
417     @details
418     Usually the entire overlay scales with scale().
419     Here we obviously have to adjust this.
420     */
421    void HUDNavigation::sizeChanged()
422    {
423        // Use size to compensate for aspect ratio if enabled.
424        float xScale = this->getActualSize().x;
425        float yScale = this->getActualSize().y;
426
427        for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it != this->activeObjectList_.end(); ++it)
428        {
429            if (it->second.panel_ != NULL)
430                it->second.panel_->setDimensions(this->navMarkerSize_ * xScale, this->navMarkerSize_ * yScale);
431            if (it->second.text_ != NULL)
432                it->second.text_->setCharHeight(it->second.text_->getCharHeight() * yScale);
433            if (it->second.target_ != NULL)
434                it->second.target_->setDimensions(this->aimMarkerSize_ * xScale, this->aimMarkerSize_ * yScale);
435        }
436    }
437
438    void HUDNavigation::addObject(RadarViewable* object)
439    {
440        if (showObject(object) == false)
441        return;
442
443        if (this->activeObjectList_.size() >= this->markerLimit_)
444        if (object == NULL)
445        return;
446
447        // Object hasn't been added yet (we know that)
448        assert(this->activeObjectList_.find(object) == this->activeObjectList_.end());
449
450        // Scales used for dimensions and text size
451        float xScale = this->getActualSize().x;
452        float yScale = this->getActualSize().y;
453
454        // Create everything needed to display the object on the radar and add it to the map
455
456        // Create arrow/marker
457        Ogre::PanelOverlayElement* panel = static_cast<Ogre::PanelOverlayElement*>( Ogre::OverlayManager::getSingleton()
458                .createOverlayElement("Panel", "HUDNavigation_navMarker_" + getUniqueNumberString()));
459        //panel->setMaterialName("Orxonox/NavTDC");
460        panel->setMaterialName(TextureGenerator::getMaterialName("tdc.png", object->getRadarObjectColour()));
461        panel->setDimensions(this->navMarkerSize_ * xScale, this->navMarkerSize_ * yScale);
462        //panel->setColour(object->getRadarObjectColour());
463
464        // Create target marker
465        Ogre::PanelOverlayElement* target = static_cast<Ogre::PanelOverlayElement*>(Ogre::OverlayManager::getSingleton()
466                    .createOverlayElement("Panel", "HUDNavigation_targetMarker_" + getUniqueNumberString()));
467        target->setMaterialName(TextureGenerator::getMaterialName("target.png", object->getRadarObjectColour()));
468        target->setDimensions(this->aimMarkerSize_ * xScale, this->aimMarkerSize_ * yScale);
469
470        // Create text
471        Ogre::TextAreaOverlayElement* text = static_cast<Ogre::TextAreaOverlayElement*>( Ogre::OverlayManager::getSingleton()
472                .createOverlayElement("TextArea", "HUDNavigation_navText_" + getUniqueNumberString()));
473        text->setFontName(this->fontName_);
474        text->setCharHeight(text->getCharHeight() * yScale);
475        text->setColour(object->getRadarObjectColour());
476
477        panel->hide();
478        target->hide();
479        text->hide();
480
481        ObjectInfo tempStruct =
482        {   panel, target, text, false, false, false};
483        this->activeObjectList_[object] = tempStruct;
484
485        this->background_->addChild(panel);
486        this->background_->addChild(target);
487        this->background_->addChild(text);
488
489        this->sortedObjectList_.push_front(std::make_pair(object, (unsigned int)0));
490    }
491
492    void HUDNavigation::removeObject(RadarViewable* viewable)
493    {
494        std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.find(viewable);
495
496        if (this->activeObjectList_.find(viewable) != this->activeObjectList_.end())
497        {
498            // Detach overlays
499            this->background_->removeChild(it->second.panel_->getName());
500            this->background_->removeChild(it->second.target_->getName());
501            this->background_->removeChild(it->second.text_->getName());
502            // Properly destroy the overlay elements (do not use delete!)
503            Ogre::OverlayManager::getSingleton().destroyOverlayElement(it->second.panel_);
504            Ogre::OverlayManager::getSingleton().destroyOverlayElement(it->second.target_);
505            Ogre::OverlayManager::getSingleton().destroyOverlayElement(it->second.text_);
506            // Remove from the list
507            this->activeObjectList_.erase(viewable);
508        }
509
510        for (std::list<std::pair<RadarViewable*, unsigned int> >::iterator listIt = this->sortedObjectList_.begin(); listIt != this->sortedObjectList_.end(); ++listIt)
511        {
512            if ((listIt->first) == viewable)
513            {
514                this->sortedObjectList_.erase(listIt);
515                break;
516            }
517        }
518    }
519
520    void HUDNavigation::objectChanged(RadarViewable* viewable)
521    {
522        // TODO: niceification neccessary ;)
523        removeObject(viewable);
524        addObject(viewable);
525    }
526
527    bool HUDNavigation::showObject(RadarViewable* rv)
528    {
529        if (rv == orxonox_cast<RadarViewable*>(this->getOwner()))
530        return false;
531        assert(rv->getWorldEntity());
532        if (rv->getWorldEntity()->isVisible() == false || rv->getRadarVisibility() == false)
533        return false;
534        return true;
535    }
536
537    void HUDNavigation::changedOwner()
538    {
539        const std::set<RadarViewable*>& respawnObjects = this->getOwner()->getScene()->getRadar()->getRadarObjects();
540        for (std::set<RadarViewable*>::const_iterator it = respawnObjects.begin(); it != respawnObjects.end(); ++it)
541        {
542            if (!(*it)->isHumanShip_)
543            this->addObject(*it);
544        }
545    }
546
547    Vector3* HUDNavigation::toAimPosition(RadarViewable* target) const
548    {
549        Vector3 wePosition = HumanController::getLocalControllerSingleton()->getControllableEntity()->getWorldPosition();
550        Vector3 targetPosition = target->getRVWorldPosition();
551        Vector3 targetSpeed = target->getRVVelocity();
552        Vector3 relativePosition = targetPosition - wePosition; //Vector from attacker to target
553
554        float p_half = relativePosition.dotProduct(targetSpeed)/(targetSpeed.squaredLength() - this->currentMunitionSpeed_ * this->currentMunitionSpeed_);
555        float time1 = -p_half + sqrt(p_half * p_half - relativePosition.squaredLength()/(targetSpeed.squaredLength() - this->currentMunitionSpeed_ * this->currentMunitionSpeed_));
556
557        Vector3* result = new Vector3(targetPosition + targetSpeed * time1);
558        return result;
559    }
560
561    void HUDNavigation::selectClosestTarget()
562    {
563        if(HUDNavigation::localHUD_s)
564        {
565            HUDNavigation::localHUD_s->closestTarget_ = true;
566            orxout() << "selectClosestTarget" << std::endl;
567        }
568    }
569
570    void HUDNavigation::selectNextTarget()
571    {
572        if(HUDNavigation::localHUD_s)
573        {
574            HUDNavigation::localHUD_s->nextTarget_ = true;
575            orxout() << "selectNextTarget" << std::endl;
576        }
577    }
578}
Note: See TracBrowser for help on using the repository browser.