Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9444 was 9444, checked in by jo, 11 years ago

The time seems correct. The marker positioning also seems to be ok. So from where comes the displacement?

  • Property svn:eol-style set to native
File size: 22.0 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 "util/Math.h"
40#include "util/Convert.h"
41#include "core/CoreIncludes.h"
42#include "core/XMLPort.h"
43#include "CameraManager.h"
44#include "Scene.h"
45#include "Radar.h"
46#include "graphics/Camera.h"
47#include "controllers/HumanController.h"
48#include "worldentities/pawns/Pawn.h"
49#include "worldentities/WorldEntity.h"
50#include "core/ConfigValueIncludes.h"
51#include "tools/TextureGenerator.h"
52// #include <boost/bind/bind_template.hpp>
53
54
55namespace orxonox
56{
57    static bool compareDistance(std::pair<RadarViewable*, unsigned int> a,
58            std::pair<RadarViewable*, unsigned int> b)
59    {
60        return a.second < b.second;
61    }
62    CreateFactory ( HUDNavigation );
63
64    HUDNavigation::HUDNavigation(BaseObject* creator) :
65        OrxonoxOverlay(creator)
66    {
67        RegisterObject(HUDNavigation)
68;        this->setConfigValues();
69
70        // Set default values
71        this->setFont("Monofur");
72        this->setTextSize(0.05f);
73        this->setNavMarkerSize(0.03f);
74        this->setAimMarkerSize(0.02f);
75
76        this->setDetectionLimit(10000.0f);
77        this->currentMunitionSpeed_ = 2500.0f;
78
79        /*Pawn* ship = orxonox_cast<Pawn*>(this->getOwner());
80        if(ship != NULL)
81            this->ship_ = ship;*/
82    }
83
84    HUDNavigation::~HUDNavigation()
85    {
86        if (this->isInitialized())
87        {
88            for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it != this->activeObjectList_.end();)
89            removeObject((it++)->first);
90        }
91        this->sortedObjectList_.clear();
92    }
93
94    void HUDNavigation::setConfigValues()
95    {
96        SetConfigValue(markerLimit_, 3);
97        SetConfigValue(showDistance_, false);
98    }
99
100    void HUDNavigation::XMLPort(Element& xmlelement, XMLPort::Mode mode)
101    {
102        SUPER(HUDNavigation, XMLPort, xmlelement, mode);
103
104        XMLPortParam(HUDNavigation, "font", setFont, getFont, xmlelement, mode);
105        XMLPortParam(HUDNavigation, "textSize", setTextSize, getTextSize, xmlelement, mode);
106        XMLPortParam(HUDNavigation, "navMarkerSize", setNavMarkerSize, getNavMarkerSize, xmlelement, mode);
107        XMLPortParam(HUDNavigation, "detectionLimit", setDetectionLimit, getDetectionLimit, xmlelement, mode);
108        XMLPortParam(HUDNavigation, "aimMarkerSize", setAimMarkerSize, getAimMarkerSize, xmlelement, mode);
109    }
110
111    void HUDNavigation::setFont(const std::string& font)
112    {
113        const Ogre::ResourcePtr& fontPtr = Ogre::FontManager::getSingleton().getByName(font);
114        if (fontPtr.isNull())
115        {
116            orxout(internal_warning) << "HUDNavigation: Font '" << font << "' not found" << endl;
117            return;
118        }
119        this->fontName_ = font;
120        for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it != this->activeObjectList_.end(); ++it)
121        {
122            if (it->second.text_ != NULL)
123            it->second.text_->setFontName(this->fontName_);
124        }
125    }
126
127    const std::string& HUDNavigation::getFont() const
128    {
129        return this->fontName_;
130    }
131
132    void HUDNavigation::setTextSize(float size)
133    {
134        if (size <= 0.0f)
135        {
136            orxout(internal_warning) << "HUDNavigation: Negative font size not allowed" << endl;
137            return;
138        }
139        this->textSize_ = size;
140        for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it!=this->activeObjectList_.end(); ++it)
141        {
142            if (it->second.text_)
143            it->second.text_->setCharHeight(size);
144        }
145    }
146
147    float HUDNavigation::getTextSize() const
148    {
149        return this->textSize_;
150    }
151
152    float HUDNavigation::getArrowSizeX(int dist) const
153    {
154        if (dist < 600)
155        dist = 600;
156        return this->getActualSize().x * 900 * this->navMarkerSize_ / dist;
157    }
158
159    float HUDNavigation::getArrowSizeY(int dist) const
160    {
161        if (dist < 600)
162        dist = 600;
163        return this->getActualSize().y * 900 * this->navMarkerSize_ / dist;
164    }
165
166    void HUDNavigation::tick(float dt)
167    {
168        SUPER(HUDNavigation, tick, dt);
169
170        Camera* cam = CameraManager::getInstance().getActiveCamera();
171        if (cam == NULL)
172        return;
173        const Matrix4& camTransform = cam->getOgreCamera()->getProjectionMatrix() * cam->getOgreCamera()->getViewMatrix();
174
175        for (std::list<std::pair<RadarViewable*, unsigned int> >::iterator listIt = this->sortedObjectList_.begin(); listIt != this->sortedObjectList_.end(); ++listIt)
176        listIt->second = (int)((listIt->first->getRVWorldPosition() - HumanController::getLocalControllerSingleton()->getControllableEntity()->getWorldPosition()).length() + 0.5f);
177
178        this->sortedObjectList_.sort(compareDistance);
179
180        unsigned int markerCount = 0;
181        bool closeEnough = false; // only display objects that are close enough to be relevant for the player
182
183
184        for (std::list<std::pair<RadarViewable*, unsigned int> >::iterator listIt = this->sortedObjectList_.begin(); listIt != this->sortedObjectList_.end(); ++markerCount, ++listIt)
185        {
186            std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.find(listIt->first);
187            closeEnough = listIt->second < this->detectionLimit_;
188            // display radarviewables on HUD if the marker limit and max-distance is not exceeded
189            if (markerCount < this->markerLimit_ && (closeEnough || this->detectionLimit_ < 0))
190            {
191                // Get Distance to HumanController and save it in the TextAreaOverlayElement.
192                int dist = listIt->second;
193                float textLength = 0.0f;
194
195                if (this->showDistance_)
196                {
197                    //display distance next to cursor
198                    it->second.text_->setCaption(multi_cast<std::string>(dist));
199                    textLength = multi_cast<std::string>(dist).size() * it->second.text_->getCharHeight() * 0.3f;
200                }
201                else
202                {
203                    //display name next to cursor
204                    it->second.text_->setCaption(it->first->getRadarName());
205                    textLength = it->first->getRadarName().size() * it->second.text_->getCharHeight() * 0.3f;
206                }
207
208                // TODO : closest object is selected
209                if(listIt == this->sortedObjectList_.begin())
210                {
211                    it->second.selected_ = true;
212                } else {
213                    it->second.selected_ = false;
214                }
215
216                // Transform to screen coordinates
217                Vector3 pos = camTransform * it->first->getRVWorldPosition();
218
219                bool outOfView = true;
220                if (pos.z > 1.0)
221                {
222                    // z > 1.0 means that the object is behind the camera
223                    outOfView = true;
224                    // we have to switch all coordinates (if you don't know why,
225                    // try linear algebra lectures, because I can't explain..)
226                    pos.x = -pos.x;
227                    pos.y = -pos.y;
228                }
229                else
230                    outOfView = pos.x < -1.0 || pos.x > 1.0 || pos.y < -1.0 || pos.y > 1.0;
231
232                if (outOfView)
233                {
234                    // Object is not in view
235
236                    // Change material only if outOfView changed
237                    if (!it->second.wasOutOfView_)
238                    {
239                        it->second.panel_->setMaterialName(TextureGenerator::getMaterialName("arrows.png", it->first->getRadarObjectColour()));
240                        it->second.wasOutOfView_ = true;
241                        it->second.target_->hide();
242                    }
243
244                    //float xDistScale = this->getActualSize().x * 1000.0f * this->navMarkerSize_ / dist;
245                    //float yDistScale = this->getActualSize().y * 1000.0f * this->navMarkerSize_ / dist;
246
247                    // Adjust Arrowsize according to distance
248                    it->second.panel_->setDimensions(getArrowSizeX(dist), getArrowSizeY(dist));
249
250                    // Switch between top, bottom, left and right position of the arrow at the screen border
251                    if (pos.x < pos.y)
252                    {
253                        if (pos.y > -pos.x)
254                        {
255                            // Top
256                            float position = pos.x / pos.y + 1.0f;
257                            it->second.panel_->setPosition((position - it->second.panel_->getWidth()) * 0.5f, 0.0f);
258                            it->second.panel_->setUV(0.5f, 0.0f, 1.0f, 0.5f);
259                            it->second.text_->setLeft((position - textLength) * 0.5f);
260                            it->second.text_->setTop(it->second.panel_->getHeight());
261                        }
262                        else
263                        {
264                            // Left
265                            float position = pos.y / pos.x + 1.0f;
266                            it->second.panel_->setPosition(0.0f, (position - it->second.panel_->getWidth()) * 0.5f);
267                            it->second.panel_->setUV(0.0f, 0.0f, 0.5f, 0.5f);
268                            it->second.text_->setLeft(it->second.panel_->getWidth() + 0.01f);
269                            it->second.text_->setTop((position - it->second.text_->getCharHeight()) * 0.5f);
270                        }
271                    }
272                    else
273                    {
274                        if (pos.y < -pos.x)
275                        {
276                            // Bottom
277                            float position = -pos.x / pos.y + 1.0f;
278                            it->second.panel_->setPosition((position - it->second.panel_->getWidth()) * 0.5f, 1.0f - it->second.panel_->getHeight());
279                            it->second.panel_->setUV(0.0f, 0.5f, 0.5f, 1.0f );
280                            it->second.text_->setLeft((position - textLength) * 0.5f);
281                            it->second.text_->setTop(1.0f - it->second.panel_->getHeight() - it->second.text_->getCharHeight());
282                        }
283                        else
284                        {
285                            // Right
286                            float position = -pos.y / pos.x + 1.0f;
287                            it->second.panel_->setPosition(1.0f - it->second.panel_->getWidth(), (position - it->second.panel_->getHeight()) * 0.5f);
288                            it->second.panel_->setUV(0.5f, 0.5f, 1.0f, 1.0f);
289                            it->second.text_->setLeft(1.0f - it->second.panel_->getWidth() - textLength - 0.01f);
290                            it->second.text_->setTop((position - it->second.text_->getCharHeight()) * 0.5f);
291                        }
292                    }
293                }
294                else
295                {
296                    // Object is in view
297
298                    // Change material only if outOfView changed
299                    if (it->second.wasOutOfView_)
300                    {
301                        //it->second.panel_->setMaterialName("Orxonox/NavTDC");
302                        it->second.panel_->setMaterialName(TextureGenerator::getMaterialName("tdc.png", it->first->getRadarObjectColour()));
303                        it->second.panel_->setDimensions(this->navMarkerSize_ * this->getActualSize().x, this->navMarkerSize_ * this->getActualSize().y);
304                        it->second.target_->setDimensions(this->aimMarkerSize_ * this->getActualSize().x, this->aimMarkerSize_ * this->getActualSize().y);
305                        it->second.wasOutOfView_ = false;
306                    }
307
308                    // Position marker
309                    it->second.panel_->setUV(0.0f, 0.0f, 1.0f, 1.0f);
310                    it->second.panel_->setLeft((pos.x + 1.0f - it->second.panel_->getWidth()) * 0.5f);
311                    it->second.panel_->setTop((-pos.y + 1.0f - it->second.panel_->getHeight()) * 0.5f);
312
313                    // Position text
314                    it->second.text_->setLeft((pos.x + 1.0f + it->second.panel_->getWidth()) * 0.5f);
315                    it->second.text_->setTop((-pos.y + 1.0f + it->second.panel_->getHeight()) * 0.5f);
316
317                    // Target marker
318                    if(it->second.selected_ && it->first->getRVOrientedVelocity().squaredLength() != 0)
319                    {
320                        Vector3* targetPos = this->toAimPosition(it->first);
321                        Vector3 screenPos = camTransform * (*targetPos);
322                        // Check if the target marker is in view too
323                        if(screenPos.z > 1 || screenPos.x < -1.0 || screenPos.x > 1.0
324                                || screenPos.y < -1.0 || screenPos.y > 1.0)
325                        {
326                            it->second.target_->hide();
327                        }
328                        else
329                        {
330                            it->second.target_->setLeft((screenPos.x + 1.0f - it->second.target_->getWidth()) * 0.5f);
331                            it->second.target_->setTop((-screenPos.y + 1.0f - it->second.target_->getHeight()) * 0.5f);
332                            it->second.target_->show();
333                        }
334
335                        delete targetPos;
336                    }
337                    else // don't show marker for not selected enemies
338                        it->second.target_->hide();
339                }
340
341                // Make sure the overlays are shown
342                it->second.panel_->show();
343                it->second.text_->show();
344            }
345            else // do not display on HUD
346
347            {
348                it->second.panel_->hide();
349                it->second.text_->hide();
350                it->second.target_->hide();
351            }
352        }
353    }
354
355    /** Overridden method of OrxonoxOverlay.
356     @details
357     Usually the entire overlay scales with scale().
358     Here we obviously have to adjust this.
359     */
360    void HUDNavigation::sizeChanged()
361    {
362        // Use size to compensate for aspect ratio if enabled.
363        float xScale = this->getActualSize().x;
364        float yScale = this->getActualSize().y;
365
366        for (std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.begin(); it != this->activeObjectList_.end(); ++it)
367        {
368            if (it->second.panel_ != NULL)
369                it->second.panel_->setDimensions(this->navMarkerSize_ * xScale, this->navMarkerSize_ * yScale);
370            if (it->second.text_ != NULL)
371                it->second.text_->setCharHeight(it->second.text_->getCharHeight() * yScale);
372            if (it->second.target_ != NULL)
373                it->second.target_->setDimensions(this->aimMarkerSize_ * xScale, this->aimMarkerSize_ * yScale);
374        }
375    }
376
377    void HUDNavigation::addObject(RadarViewable* object)
378    {
379        if (showObject(object) == false)
380        return;
381
382        if (this->activeObjectList_.size() >= this->markerLimit_)
383        if (object == NULL)
384        return;
385
386        // Object hasn't been added yet (we know that)
387        assert(this->activeObjectList_.find(object) == this->activeObjectList_.end());
388
389        // Scales used for dimensions and text size
390        float xScale = this->getActualSize().x;
391        float yScale = this->getActualSize().y;
392
393        // Create everything needed to display the object on the radar and add it to the map
394
395        // Create arrow/marker
396        Ogre::PanelOverlayElement* panel = static_cast<Ogre::PanelOverlayElement*>( Ogre::OverlayManager::getSingleton()
397                .createOverlayElement("Panel", "HUDNavigation_navMarker_" + getUniqueNumberString()));
398        //panel->setMaterialName("Orxonox/NavTDC");
399        panel->setMaterialName(TextureGenerator::getMaterialName("tdc.png", object->getRadarObjectColour()));
400        panel->setDimensions(this->navMarkerSize_ * xScale, this->navMarkerSize_ * yScale);
401        //panel->setColour(object->getRadarObjectColour());
402
403        // Create target marker
404        Ogre::PanelOverlayElement* target = static_cast<Ogre::PanelOverlayElement*>(Ogre::OverlayManager::getSingleton()
405                .createOverlayElement("Panel", "HUDNavigation_targetMarker_" + getUniqueNumberString()));
406        target->setMaterialName(TextureGenerator::getMaterialName("target.png", object->getRadarObjectColour()));
407        target->setDimensions(this->aimMarkerSize_ * xScale, this->aimMarkerSize_ * yScale);
408
409        // Create text
410        Ogre::TextAreaOverlayElement* text = static_cast<Ogre::TextAreaOverlayElement*>( Ogre::OverlayManager::getSingleton()
411                .createOverlayElement("TextArea", "HUDNavigation_navText_" + getUniqueNumberString()));
412        text->setFontName(this->fontName_);
413        text->setCharHeight(text->getCharHeight() * yScale);
414        text->setColour(object->getRadarObjectColour());
415
416        panel->hide();
417        target->hide();
418        text->hide();
419
420        ObjectInfo tempStruct =
421        {   panel, target, text, false, false, false};
422        this->activeObjectList_[object] = tempStruct;
423
424        this->background_->addChild(panel);
425        this->background_->addChild(target);
426        this->background_->addChild(text);
427
428        this->sortedObjectList_.push_front(std::make_pair(object, (unsigned int)0));
429    }
430
431    void HUDNavigation::removeObject(RadarViewable* viewable)
432    {
433        std::map<RadarViewable*, ObjectInfo>::iterator it = this->activeObjectList_.find(viewable);
434
435        if (this->activeObjectList_.find(viewable) != this->activeObjectList_.end())
436        {
437            // Detach overlays
438            this->background_->removeChild(it->second.panel_->getName());
439            this->background_->removeChild(it->second.target_->getName());
440            this->background_->removeChild(it->second.text_->getName());
441            // Properly destroy the overlay elements (do not use delete!)
442            Ogre::OverlayManager::getSingleton().destroyOverlayElement(it->second.panel_);
443            Ogre::OverlayManager::getSingleton().destroyOverlayElement(it->second.target_);
444            Ogre::OverlayManager::getSingleton().destroyOverlayElement(it->second.text_);
445            // Remove from the list
446            this->activeObjectList_.erase(viewable);
447        }
448
449        for (std::list<std::pair<RadarViewable*, unsigned int> >::iterator listIt = this->sortedObjectList_.begin(); listIt != this->sortedObjectList_.end(); ++listIt)
450        {
451            if ((listIt->first) == viewable)
452            {
453                this->sortedObjectList_.erase(listIt);
454                break;
455            }
456        }
457    }
458
459    void HUDNavigation::objectChanged(RadarViewable* viewable)
460    {
461        // TODO: niceification neccessary ;)
462        removeObject(viewable);
463        addObject(viewable);
464    }
465
466    bool HUDNavigation::showObject(RadarViewable* rv)
467    {
468        if (rv == orxonox_cast<RadarViewable*>(this->getOwner()))
469        return false;
470        assert(rv->getWorldEntity());
471        if (rv->getWorldEntity()->isVisible() == false || rv->getRadarVisibility() == false)
472        return false;
473        return true;
474    }
475
476    void HUDNavigation::changedOwner()
477    {
478        const std::set<RadarViewable*>& respawnObjects = this->getOwner()->getScene()->getRadar()->getRadarObjects();
479        for (std::set<RadarViewable*>::const_iterator it = respawnObjects.begin(); it != respawnObjects.end(); ++it)
480        {
481            if (!(*it)->isHumanShip_)
482            this->addObject(*it);
483        }
484    }
485
486    Vector3* HUDNavigation::toAimPosition(RadarViewable* target) const
487    {
488        Vector3 wePosition = HumanController::getLocalControllerSingleton()->getControllableEntity()->getWorldPosition();
489        Vector3 targetPosition = target->getRVWorldPosition();
490        Vector3 targetSpeed = target->getRVOrientedVelocity();
491        Vector3 relativePosition = targetPosition - wePosition; //Vector from attacker to target
492
493        float p_half = relativePosition.dotProduct(targetSpeed)/(targetSpeed.squaredLength() - this->currentMunitionSpeed_ * this->currentMunitionSpeed_);
494        float time1 = -p_half + sqrt(p_half * p_half - relativePosition.squaredLength()/(targetSpeed.squaredLength() - this->currentMunitionSpeed_ * this->currentMunitionSpeed_));
495        orxout()<< "TIME 1: " << time1 <<endl;
496
497
498
499        // munSpeed*time = lengthBetween(wePosition, targetPosition + targetSpeed*time)
500        // from this we extract:
501        float a = pow(targetSpeed.length(),2) - pow(this->currentMunitionSpeed_,2);
502        float b = 2*((targetPosition.x - wePosition.x)*targetSpeed.x
503                    +(targetPosition.y - wePosition.y)*targetSpeed.y
504                    +(targetPosition.z - wePosition.z)*targetSpeed.z);
505        float c = pow((targetPosition-wePosition).length(),2);
506
507        // calculate smallest time solution, in case it exists
508        if(pow(b,2) - 4*a*c < 0)
509            return NULL;
510        float time = (-b - sqrt(pow(b,2) - 4*a*c))/(2*a);
511        orxout()<< "TIME 1: " << time1 <<endl;
512        if(time < 0)
513            time = (-b + sqrt(pow(b,2) - 4*a*c))/(2*a);
514        if(time < 0)
515            return NULL;
516        Vector3* result = new Vector3(targetPosition + targetSpeed * time);
517        return result;
518    }
519}
Note: See TracBrowser for help on using the repository browser.