Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/overlays/console/InGameConsole.cc @ 1633

Last change on this file since 1633 was 1633, checked in by rgrieder, 16 years ago
  • added font generation for the console (so that we can create the exact font size, looks sharper)
  • fixed 2 bugs with the size of an OrxonoxOverlay
  • fixed a bug with the visibility of entire OverlayGroups
  • Property svn:eol-style set to native
File size: 21.8 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 *      Fabian 'x3n' Landau
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30
31#include "InGameConsole.h"
32
33#include <string>
34#include <OgreOverlay.h>
35#include <OgreOverlayElement.h>
36#include <OgreOverlayManager.h>
37#include <OgreOverlayContainer.h>
38#include <OgreFontManager.h>
39#include <OgreFont.h>
40
41#include "util/Math.h"
42#include "util/Convert.h"
43#include "core/Debug.h"
44#include "core/CoreIncludes.h"
45#include "core/ConfigValueIncludes.h"
46#include "core/ConsoleCommand.h"
47#include "core/input/InputManager.h"
48#include "GraphicsEngine.h"
49
50#define LINES 30
51#define CHAR_WIDTH 7.45 // fix this please - determine the char-width dynamically
52
53namespace orxonox
54{
55    SetConsoleCommand(InGameConsole, openConsole, true);
56    SetConsoleCommand(InGameConsole, closeConsole, true);
57
58    /**
59        @brief Constructor: Creates and initializes the InGameConsole.
60    */
61    InGameConsole::InGameConsole() :
62        consoleOverlay_(0), consoleOverlayContainer_(0),
63        consoleOverlayNoise_(0), consoleOverlayCursor_(0), consoleOverlayBorder_(0),
64        consoleOverlayTextAreas_(0)
65    {
66        RegisterObject(InGameConsole);
67
68        this->bActive_ = false;
69        this->cursor_ = 0.0f;
70        this->cursorSymbol_ = '|';
71        this->inputWindowStart_ = 0;
72        this->numLinesShifted_ = LINES - 1;
73        // for the beginning, don't scroll
74        this->scroll_ = 0;
75
76        this->setConfigValues();
77    }
78
79    /**
80        @brief Destructor: Destroys the TextAreas.
81    */
82    InGameConsole::~InGameConsole(void)
83    {
84        this->destroy();
85    }
86
87    /**
88        @brief Returns a reference to the only existing instance of InGameConsole.
89    */
90    InGameConsole& InGameConsole::getInstance()
91    {
92        static InGameConsole instance;
93        return instance;
94    }
95
96    /**
97        @brief Sets the config values, describing the size of the console.
98    */
99    void InGameConsole::setConfigValues()
100    {
101        SetConfigValue(relativeWidth, 0.8);
102        SetConfigValue(relativeHeight, 0.4);
103        SetConfigValue(blinkTime, 0.5);
104        SetConfigValue(scrollSpeed_, 3.0f);
105        SetConfigValue(noiseSize_, 1.0f);
106        SetConfigValue(cursorSymbol_, '|');
107    }
108
109    /**
110        @brief Initializes the InGameConsole.
111    */
112    void InGameConsole::initialise()
113    {
114        // create overlay and elements
115        Ogre::OverlayManager* ovMan = Ogre::OverlayManager::getSingletonPtr();
116
117        // create actual overlay
118        this->consoleOverlay_ = ovMan->create("InGameConsoleConsole");
119
120        // create a container
121        this->consoleOverlayContainer_ = static_cast<Ogre::OverlayContainer*>(ovMan->createOverlayElement("Panel", "InGameConsoleContainer"));
122        this->consoleOverlayContainer_->setMetricsMode(Ogre::GMM_RELATIVE);
123        this->consoleOverlayContainer_->setPosition((1 - this->relativeWidth) / 2, 0);
124        this->consoleOverlayContainer_->setDimensions(this->relativeWidth, this->relativeHeight);
125        this->consoleOverlay_->add2D(this->consoleOverlayContainer_);
126
127        // create BorderPanel
128        this->consoleOverlayBorder_ = static_cast<Ogre::BorderPanelOverlayElement*>(ovMan->createOverlayElement("BorderPanel", "InGameConsoleBorderPanel"));
129        this->consoleOverlayBorder_->setMetricsMode(Ogre::GMM_PIXELS);
130        this->consoleOverlayBorder_->setMaterialName("ConsoleCenter");
131        this->consoleOverlayBorder_->setBorderSize(16, 16, 0, 16);
132        this->consoleOverlayBorder_->setBorderMaterialName("ConsoleBorder");
133        this->consoleOverlayBorder_->setLeftBorderUV(0.0, 0.49, 0.5, 0.51);
134        this->consoleOverlayBorder_->setRightBorderUV(0.5, 0.49, 1.0, 0.5);
135        this->consoleOverlayBorder_->setBottomBorderUV(0.49, 0.5, 0.51, 1.0);
136        this->consoleOverlayBorder_->setBottomLeftBorderUV(0.0, 0.5, 0.5, 1.0);
137        this->consoleOverlayBorder_->setBottomRightBorderUV(0.5, 0.5, 1.0, 1.0);
138        this->consoleOverlayContainer_->addChild(this->consoleOverlayBorder_);
139
140        // create a new font to match the requested size exactly
141        Ogre::FontPtr font = static_cast<Ogre::FontPtr>
142            (Ogre::FontManager::getSingleton().create("MonofurConsole", "General"));
143        font->setType(Ogre::FT_TRUETYPE);
144        font->setSource("Monofur.ttf");
145        font->setTrueTypeSize(18);
146        // reto: I don't know why, but setting the resolution twice as high makes the font look a lot clearer
147        font->setTrueTypeResolution(192);
148        font->addCodePointRange(Ogre::Font::CodePointRange(33, 126));
149        font->addCodePointRange(Ogre::Font::CodePointRange(161, 255));
150
151        // create the text lines
152        this->consoleOverlayTextAreas_ = new Ogre::TextAreaOverlayElement*[LINES];
153        for (int i = 0; i < LINES; i++)
154        {
155            this->consoleOverlayTextAreas_[i] = static_cast<Ogre::TextAreaOverlayElement*>(ovMan->createOverlayElement("TextArea", "InGameConsoleTextArea" + convertToString(i)));
156            this->consoleOverlayTextAreas_[i]->setMetricsMode(Ogre::GMM_PIXELS);
157            this->consoleOverlayTextAreas_[i]->setFontName("MonofurConsole");
158            this->consoleOverlayTextAreas_[i]->setCharHeight(18);
159            this->consoleOverlayTextAreas_[i]->setParameter("colour_top", "0.21 0.69 0.21");
160            this->consoleOverlayTextAreas_[i]->setLeft(8);
161            this->consoleOverlayTextAreas_[i]->setCaption("");
162            this->consoleOverlayContainer_->addChild(this->consoleOverlayTextAreas_[i]);
163        }
164
165        // create cursor (also a text area overlay element)
166        this->consoleOverlayCursor_ = static_cast<Ogre::TextAreaOverlayElement*>(ovMan->createOverlayElement("TextArea", "InGameConsoleCursor"));
167        this->consoleOverlayCursor_->setMetricsMode(Ogre::GMM_PIXELS);
168        this->consoleOverlayCursor_->setFontName("MonofurConsole");
169        this->consoleOverlayCursor_->setCharHeight(18);
170        this->consoleOverlayCursor_->setParameter("colour_top", "0.21 0.69 0.21");
171        this->consoleOverlayCursor_->setLeft(7);
172        this->consoleOverlayCursor_->setCaption(std::string(this->cursorSymbol_, 1));
173        this->consoleOverlayContainer_->addChild(this->consoleOverlayCursor_);
174
175        // create noise
176        this->consoleOverlayNoise_ = static_cast<Ogre::PanelOverlayElement*>(ovMan->createOverlayElement("Panel", "InGameConsoleNoise"));
177        this->consoleOverlayNoise_->setMetricsMode(Ogre::GMM_PIXELS);
178        this->consoleOverlayNoise_->setPosition(5,0);
179        this->consoleOverlayNoise_->setMaterialName("ConsoleNoiseSmall");
180        // comment following line to disable noise
181        this->consoleOverlayContainer_->addChild(this->consoleOverlayNoise_);
182
183        this->windowResized(GraphicsEngine::getSingleton().getWindowWidth(), GraphicsEngine::getSingleton().getWindowHeight());
184
185        // move overlay "above" the top edge of the screen
186        // we take -1.2 because the border makes the panel bigger
187        this->consoleOverlayContainer_->setTop(-1.2 * this->relativeHeight);
188
189        Shell::getInstance().addOutputLevel(true);
190
191        COUT(4) << "Info: InGameConsole initialized" << std::endl;
192    }
193
194    /**
195        @brief Destroys all the elements if necessary.
196    */
197    void InGameConsole::destroy()
198    {
199        Ogre::OverlayManager* ovMan = Ogre::OverlayManager::getSingletonPtr();
200        if (ovMan)
201        {
202            if (this->consoleOverlayNoise_)
203                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayNoise_);
204            if (this->consoleOverlayCursor_)
205                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayCursor_);
206            if (this->consoleOverlayBorder_)
207                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayBorder_);
208            if (this->consoleOverlayTextAreas_)
209            {
210                for (int i = 0; i < LINES; i++)
211                {
212                    if (this->consoleOverlayTextAreas_[i])
213                      Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayTextAreas_[i]);
214                    this->consoleOverlayTextAreas_[i] = 0;
215                }
216
217            }
218            if (this->consoleOverlayContainer_)
219                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayContainer_);
220        }
221        if (this->consoleOverlayTextAreas_)
222        {
223            delete[] this->consoleOverlayTextAreas_;
224            this->consoleOverlayTextAreas_ = 0;
225        }
226    }
227
228    // ###############################
229    // ###  ShellListener methods  ###
230    // ###############################
231
232    /**
233        @brief Called if all output-lines have to be redrawn.
234    */
235    void InGameConsole::linesChanged()
236    {
237        std::list<std::string>::const_iterator it = Shell::getInstance().getNewestLineIterator();
238        int max = 0;
239        for (int i = 1; i < LINES; ++i)
240        {
241            if (it != Shell::getInstance().getEndIterator())
242            {
243                ++it;
244                max = i;
245            }
246            else
247                break;
248        }
249
250        for (int i = LINES - 1; i > max; --i)
251            this->print("", i, true);
252
253        for (int i = max; i >= 1; --i)
254        {
255            --it;
256            this->print(*it, i, true);
257        }
258    }
259
260    /**
261        @brief Called if only the last output-line has changed.
262    */
263    void InGameConsole::onlyLastLineChanged()
264    {
265        if (LINES > 1)
266            this->print(*Shell::getInstance().getNewestLineIterator(), 1);
267    }
268
269    /**
270        @brief Called if a new output-line was added.
271    */
272    void InGameConsole::lineAdded()
273    {
274        this->numLinesShifted_ = 0;
275        this->shiftLines();
276        this->onlyLastLineChanged();
277    }
278
279    /**
280        @brief Called if the text in the input-line has changed.
281    */
282    void InGameConsole::inputChanged()
283    {
284        if (LINES > 0)
285            this->print(Shell::getInstance().getInput(), 0);
286
287        if (Shell::getInstance().getInput() == "" || Shell::getInstance().getInput().size() == 0)
288            this->inputWindowStart_ = 0;
289    }
290
291    /**
292        @brief Called if the position of the cursor in the input-line has changed.
293    */
294    void InGameConsole::cursorChanged()
295    {
296        unsigned int pos = Shell::getInstance().getCursorPosition() - inputWindowStart_;
297        if (pos > maxCharsPerLine_)
298            pos = maxCharsPerLine_;
299        else if (pos < 0)
300            pos = 0;
301
302        this->consoleOverlayCursor_->setCaption(std::string(pos,' ') + cursorSymbol_);
303        this->consoleOverlayCursor_->setTop((int) this->windowH_ * this->relativeHeight - 24);
304    }
305
306    /**
307        @brief Called if the console gets closed.
308    */
309    void InGameConsole::exit()
310    {
311        this->deactivate();
312    }
313
314    // ###############################
315    // ###  other external calls   ###
316    // ###############################
317
318    /**
319        @brief Used to control the actual scrolling and the cursor.
320    */
321    void InGameConsole::tick(float dt)
322    {
323        if (this->scroll_ != 0)
324        {
325            float oldTop = this->consoleOverlayContainer_->getTop();
326
327            if (this->scroll_ > 0)
328            {
329                // scrolling down
330                // enlarge oldTop a little bit so that this exponential function
331                // reaches 0 before infinite time has passed...
332                float deltaScroll = (oldTop - 0.01) * dt * this->scrollSpeed_;
333                if (oldTop - deltaScroll >= 0)
334                {
335                    // window has completely scrolled down
336                    this->consoleOverlayContainer_->setTop(0);
337                    this->scroll_ = 0;
338                }
339                else
340                    this->consoleOverlayContainer_->setTop(oldTop - deltaScroll);
341            }
342
343            else
344            {
345                // scrolling up
346                // note: +0.01 for the same reason as when scrolling down
347                float deltaScroll = (1.2 * this->relativeHeight + 0.01 + oldTop) * dt * this->scrollSpeed_;
348                if (oldTop - deltaScroll <= -1.2 * this->relativeHeight)
349                {
350                    // window has completely scrolled up
351                    this->consoleOverlayContainer_->setTop(-1.2 * this->relativeHeight);
352                    this->scroll_ = 0;
353                    this->consoleOverlay_->hide();
354                }
355                else
356                    this->consoleOverlayContainer_->setTop(oldTop - deltaScroll);
357            }
358        }
359
360        if (this->bActive_)
361        {
362            this->cursor_ += dt;
363            if (this->cursor_ >= this->blinkTime)
364            {
365                this->cursor_ = 0;
366                bShowCursor_ = !bShowCursor_;
367                if (bShowCursor_)
368                    this->consoleOverlayCursor_->show();
369                else
370                    this->consoleOverlayCursor_->hide();
371            }
372
373            // this creates a flickering effect (extracts exactly 80% of the texture at a random location)
374            float uRand = (rand() & 1023) / 1023.0f * 0.2f;
375            float vRand = (rand() & 1023) / 1023.0f * 0.2f;
376            this->consoleOverlayNoise_->setUV(uRand, vRand, 0.8f + uRand, 0.8f + vRand);
377        }
378    }
379
380    /**
381        @brief Resizes the console elements. Call if window size changes.
382    */
383    void InGameConsole::windowResized(int newWidth, int newHeight)
384    {
385        this->windowW_ = newWidth;
386        this->windowH_ = newHeight;
387        this->consoleOverlayBorder_->setWidth((int) this->windowW_* this->relativeWidth);
388        this->consoleOverlayBorder_->setHeight((int) this->windowH_ * this->relativeHeight);
389        this->consoleOverlayNoise_->setWidth((int) this->windowW_ * this->relativeWidth - 10);
390        this->consoleOverlayNoise_->setHeight((int) this->windowH_ * this->relativeHeight - 5);
391        this->consoleOverlayNoise_->setTiling(consoleOverlayNoise_->getWidth() / (50.0f * this->noiseSize_), consoleOverlayNoise_->getHeight() / (50.0f * this->noiseSize_));
392
393        // now adjust the text lines...
394        this->desiredTextWidth_ = (int) (this->windowW_ * this->relativeWidth) - 12;
395
396        if (LINES > 0)
397            this->maxCharsPerLine_ = max((unsigned int)10, (unsigned int) ((float)this->desiredTextWidth_ / CHAR_WIDTH));
398        else
399            this->maxCharsPerLine_ = 10;
400
401        for (int i = 0; i < LINES; i++)
402        {
403            this->consoleOverlayTextAreas_[i]->setWidth(this->desiredTextWidth_);
404            this->consoleOverlayTextAreas_[i]->setTop((int) this->windowH_ * this->relativeHeight - 24 - 14*i);
405        }
406
407        this->linesChanged();
408        this->cursorChanged();
409    }
410
411    // ###############################
412    // ###    internal methods     ###
413    // ###############################
414
415    /**
416        @brief Prints string to bottom line.
417        @param s String to be printed
418    */
419    void InGameConsole::print(const std::string& text, int index, bool alwaysShift)
420    {
421        char level = 0;
422        if (text.size() > 0)
423            level = text[0];
424
425        std::string output = text;
426
427        if (level >= -1 && level <= 5)
428            output.erase(0, 1);
429
430        if (LINES > index)
431        {
432            this->colourLine(level, index);
433
434            if (index > 0)
435            {
436                unsigned int linesUsed = 1;
437                while (output.size() > this->maxCharsPerLine_)
438                {
439                    ++linesUsed;
440                    this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output.substr(0, this->maxCharsPerLine_)));
441                    output.erase(0, this->maxCharsPerLine_);
442                    output.insert(0, 1, ' ');
443                    if (linesUsed > numLinesShifted_ || alwaysShift)
444                        this->shiftLines();
445                    this->colourLine(level, index);
446                }
447                this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output));
448                this->displayedText_ = output;
449                this->numLinesShifted_ = linesUsed;
450            }
451            else
452            {
453                if (output.size() > this->maxCharsPerLine_)
454                {
455                    if (Shell::getInstance().getInputBuffer().getCursorPosition() < this->inputWindowStart_)
456                        this->inputWindowStart_ = Shell::getInstance().getInputBuffer().getCursorPosition();
457                    else if (Shell::getInstance().getInputBuffer().getCursorPosition() >= (this->inputWindowStart_ + this->maxCharsPerLine_ - 1))
458                        this->inputWindowStart_ = Shell::getInstance().getInputBuffer().getCursorPosition() - this->maxCharsPerLine_ + 1;
459
460                    output = output.substr(this->inputWindowStart_, this->maxCharsPerLine_);
461                }
462                else
463                  this->inputWindowStart_ = 0;
464                this->displayedText_ = output;
465                this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output));
466            }
467        }
468    }
469
470    /**
471        @brief Shows the InGameConsole.
472    */
473    void InGameConsole::activate()
474    {
475        if (!this->bActive_)
476        {
477            this->bActive_ = true;
478            InputManager::setInputState(InputManager::IS_CONSOLE);
479            Shell::getInstance().registerListener(this);
480
481            this->windowResized(this->windowW_, this->windowH_);
482            this->linesChanged();
483            this->cursorChanged();
484            this->consoleOverlay_->show();
485
486            // scroll down
487            this->scroll_ = 1;
488            // the rest is done by tick
489        }
490    }
491
492    /**
493    @brief Hides the InGameConsole.
494    */
495    void InGameConsole::deactivate()
496    {
497        if (this->bActive_)
498        {
499            this->bActive_ = false;
500            InputManager::setInputState(InputManager::IS_NORMAL);
501            Shell::getInstance().unregisterListener(this);
502
503            // scroll up
504            this->scroll_ = -1;
505            // the rest is done by tick
506        }
507    }
508
509    /**
510        @brief Shifts all output lines one line up
511    */
512    void InGameConsole::shiftLines()
513    {
514        for (unsigned int i = LINES - 1; i > 1; --i)
515        {
516            this->consoleOverlayTextAreas_[i]->setCaption(this->consoleOverlayTextAreas_[i - 1]->getCaption());
517            this->consoleOverlayTextAreas_[i]->setColourTop(this->consoleOverlayTextAreas_[i - 1]->getColourTop());
518            this->consoleOverlayTextAreas_[i]->setColourBottom(this->consoleOverlayTextAreas_[i - 1]->getColourBottom());
519        }
520    }
521
522    void InGameConsole::colourLine(int colourcode, int index)
523    {
524        if (colourcode == -1)
525        {
526            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.90, 0.90, 0.90, 1.00));
527            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 1.00, 1.00, 1.00));
528        }
529        else if (colourcode == 1)
530        {
531            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.95, 0.25, 0.25, 1.00));
532            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.50, 0.50, 1.00));
533        }
534        else if (colourcode == 2)
535        {
536            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.95, 0.50, 0.20, 1.00));
537            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.70, 0.50, 1.00));
538        }
539        else if (colourcode == 3)
540        {
541            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.50, 0.50, 0.95, 1.00));
542            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 0.80, 1.00, 1.00));
543        }
544        else if (colourcode == 4)
545        {
546            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.65, 0.48, 0.44, 1.00));
547            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.90, 0.90, 1.00));
548        }
549        else if (colourcode == 5)
550        {
551            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.40, 0.20, 0.40, 1.00));
552            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 0.60, 0.80, 1.00));
553        }
554        else
555        {
556            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.21, 0.69, 0.21, 1.00));
557            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 1.00, 0.80, 1.00));
558        }
559    }
560
561    // ###############################
562    // ###      satic methods      ###
563    // ###############################
564
565    /**
566        @brief Activates the console.
567    */
568    /*static*/ void InGameConsole::openConsole()
569    {
570        InGameConsole::getInstance().activate();
571    }
572
573    /**
574        @brief Deactivates the console.
575    */
576    /*static*/ void InGameConsole::closeConsole()
577    {
578        InGameConsole::getInstance().deactivate();
579    }
580
581    /**
582        @brief Converts a string into an Ogre::UTFString.
583        @param s The string to convert
584        @return The converted string
585    */
586    /*static*/ Ogre::UTFString InGameConsole::convert2UTF(std::string s)
587    {
588        Ogre::UTFString utf;
589        Ogre::UTFString::code_point cp;
590        for (unsigned int i = 0; i < s.size(); ++i)
591        {
592          cp = s[i];
593          cp &= 0xFF;
594          utf.append(1, cp);
595        }
596        return utf;
597    }
598}
Note: See TracBrowser for help on using the repository browser.