Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/core/input/KeyBinder.cc @ 2800

Last change on this file since 2800 was 2800, checked in by rgrieder, 15 years ago

Renaming "tick" to "update" for all those classes not inheriting from Tickable to avoid confusions.
GameState::ticked still exists, but that's going to change anyway.

  • Property svn:eol-style set to native
File size: 18.5 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 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30 @file
31 @brief Implementation of the different input handlers.
32 */
33
34#include "KeyBinder.h"
35
36#include <fstream>
37#include <string>
38
39#include "util/Convert.h"
40#include "util/Debug.h"
41#include "core/ConfigValueIncludes.h"
42#include "core/CoreIncludes.h"
43#include "core/ConfigFileManager.h"
44#include "core/Core.h"
45#include "InputCommands.h"
46#include "InputManager.h"
47
48namespace orxonox
49{
50    /**
51    @brief
52        Constructor that does as little as necessary.
53    */
54    KeyBinder::KeyBinder()
55        : numberOfJoySticks_(0)
56        , deriveTime_(0.0f)
57    {
58        mouseRelative_[0] = 0;
59        mouseRelative_[1] = 0;
60        mousePosition_[0] = 0;
61        mousePosition_[1] = 0;
62
63        RegisterRootObject(KeyBinder);
64
65        // intialise all buttons and half axes to avoid creating everything with 'new'
66        // keys
67        for (unsigned int i = 0; i < KeyCode::numberOfKeys; i++)
68        {
69            std::string keyname = KeyCode::ByString[i];
70            if (!keyname.empty())
71                keys_[i].name_ = std::string("Key") + keyname;
72            else
73                keys_[i].name_ = "";
74            keys_[i].paramCommandBuffer_ = &paramCommandBuffer_;
75            keys_[i].groupName_ = "Keys";
76        }
77        // mouse buttons plus 4 mouse wheel buttons only 'generated' by KeyBinder
78        const char* const mouseWheelNames[] = { "Wheel1Down", "Wheel1Up", "Wheel2Down", "Wheel2Up" };
79        for (unsigned int i = 0; i < numberOfMouseButtons_; i++)
80        {
81            std::string nameSuffix;
82            if (i < MouseButtonCode::numberOfButtons)
83                nameSuffix = MouseButtonCode::ByString[i];
84            else
85                nameSuffix = mouseWheelNames[i - MouseButtonCode::numberOfButtons];
86            mouseButtons_[i].name_ = std::string("Mouse") + nameSuffix;
87            mouseButtons_[i].paramCommandBuffer_ = &paramCommandBuffer_;
88            mouseButtons_[i].groupName_ = "MouseButtons";
89        }
90        // mouse axes
91        for (unsigned int i = 0; i < MouseAxisCode::numberOfAxes * 2; i++)
92        {
93            mouseAxes_[i].name_ = std::string("Mouse") + MouseAxisCode::ByString[i / 2];
94            if (i & 1)
95                mouseAxes_[i].name_ += "Pos";
96            else
97                mouseAxes_[i].name_ += "Neg";
98            mouseAxes_[i].paramCommandBuffer_ = &paramCommandBuffer_;
99            mouseAxes_[i].groupName_ = "MouseAxes";
100        }
101
102        // Get a new ConfigFileType from the ConfigFileManager
103        this->configFile_ = ConfigFileManager::getInstance().getNewConfigFileType();
104
105        // initialise joy sticks separatly to allow for reloading
106        numberOfJoySticks_ = InputManager::getInstance().numberOfJoySticks();
107        initialiseJoyStickBindings();
108
109        // collect all Buttons and HalfAxes
110        compilePointerLists();
111
112        // set them here to use allHalfAxes_
113        setConfigValues();
114    }
115
116    /**
117    @brief
118        Destructor
119    */
120    KeyBinder::~KeyBinder()
121    {
122        // almost no destructors required because most of the arrays are static.
123        clearBindings(); // does some destruction work
124    }
125
126    /**
127    @brief
128        Loader for the key bindings, managed by config values.
129    */
130    void KeyBinder::setConfigValues()
131    {
132        SetConfigValue(analogThreshold_, 0.05f)
133            .description("Threshold for analog axes until which the state is 0.");
134        SetConfigValue(bFilterAnalogNoise_, false)
135            .description("Specifies whether to filter small analog values like joy stick fluctuations.");
136        SetConfigValue(mouseSensitivity_, 1.0f)
137            .description("Mouse sensitivity.");
138        SetConfigValue(bDeriveMouseInput_, false)
139            .description("Whether or not to derive moues movement for the absolute value.");
140        SetConfigValue(derivePeriod_, 0.05f)
141            .description("Accuracy of the mouse input deriver. The higher the more precise, but laggier.");
142        SetConfigValue(mouseSensitivityDerived_, 1.0f)
143            .description("Mouse sensitivity if mouse input is derived.");
144        SetConfigValue(mouseWheelStepSize_, 120)
145            .description("Equals one step of the mousewheel.");
146        SetConfigValue(buttonThreshold_, 0.80f)
147            .description("Threshold for analog axes until which the button is not pressed.")
148            .callback(this, &KeyBinder::buttonThresholdChanged);
149    }
150
151    void KeyBinder::buttonThresholdChanged()
152    {
153        for (unsigned int i = 0; i < allHalfAxes_.size(); i++)
154            if (!allHalfAxes_[i]->bButtonThresholdUser_)
155                allHalfAxes_[i]->buttonThreshold_ = this->buttonThreshold_;
156    }
157
158    void KeyBinder::JoyStickDeviceNumberChanged(unsigned int value)
159    {
160        unsigned int oldValue = numberOfJoySticks_;
161        numberOfJoySticks_ = value;
162
163        // initialise joy stick bindings
164        initialiseJoyStickBindings();
165
166        // collect all Buttons and HalfAxes again
167        compilePointerLists();
168
169        // load the bindings if required
170        if (configFile_ != ConfigFileType::NoType)
171        {
172            for (unsigned int iDev = oldValue; iDev < numberOfJoySticks_; ++iDev)
173            {
174                for (unsigned int i = 0; i < JoyStickButtonCode::numberOfButtons; ++i)
175                    joyStickButtons_[iDev][i].readConfigValue(this->configFile_);
176                for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; ++i)
177                    joyStickAxes_[iDev][i].readConfigValue(this->configFile_);
178            }
179        }
180
181        // Set the button threshold for potential new axes
182        buttonThresholdChanged();
183    }
184
185    void KeyBinder::initialiseJoyStickBindings()
186    {
187        this->joyStickAxes_.resize(numberOfJoySticks_);
188        this->joyStickButtons_.resize(numberOfJoySticks_);
189
190        // reinitialise all joy stick binings (doesn't overwrite the old ones)
191        for (unsigned int iDev = 0; iDev < numberOfJoySticks_; iDev++)
192        {
193            std::string deviceNumber = convertToString(iDev);
194            // joy stick buttons
195            for (unsigned int i = 0; i < JoyStickButtonCode::numberOfButtons; i++)
196            {
197                joyStickButtons_[iDev][i].name_ = std::string("JoyStick") + deviceNumber + JoyStickButtonCode::ByString[i];
198                joyStickButtons_[iDev][i].paramCommandBuffer_ = &paramCommandBuffer_;
199                joyStickButtons_[iDev][i].groupName_ = std::string("JoyStick") + deviceNumber + "Buttons";
200            }
201            // joy stick axes
202            for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
203            {
204                joyStickAxes_[iDev][i].name_ = std::string("JoyStick") + deviceNumber + JoyStickAxisCode::ByString[i >> 1];
205                if (i & 1)
206                    joyStickAxes_[iDev][i].name_ += "Pos";
207                else
208                    joyStickAxes_[iDev][i].name_ += "Neg";
209                joyStickAxes_[iDev][i].paramCommandBuffer_ = &paramCommandBuffer_;
210                joyStickAxes_[iDev][i].groupName_ = std::string("JoyStick") + deviceNumber + "Axes";
211            }
212        }
213    }
214
215    void KeyBinder::compilePointerLists()
216    {
217        allButtons_.clear();
218        allHalfAxes_.clear();
219
220        // Note: Don't include the dummy keys which don't actually exist in OIS but have a number
221        for (unsigned int i = 0; i < KeyCode::numberOfKeys; i++)
222            if (!keys_[i].name_.empty())
223                allButtons_[keys_[i].name_] = keys_ + i;
224        for (unsigned int i = 0; i < numberOfMouseButtons_; i++)
225            allButtons_[mouseButtons_[i].name_] = mouseButtons_ + i;
226        for (unsigned int i = 0; i < MouseAxisCode::numberOfAxes * 2; i++)
227        {
228            allButtons_[mouseAxes_[i].name_] = mouseAxes_ + i;
229            allHalfAxes_.push_back(mouseAxes_ + i);
230        }
231        for (unsigned int iDev = 0; iDev < numberOfJoySticks_; iDev++)
232        {
233            for (unsigned int i = 0; i < JoyStickButtonCode::numberOfButtons; i++)
234                allButtons_[joyStickButtons_[iDev][i].name_] = &(joyStickButtons_[iDev][i]);
235            for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
236            {
237                allButtons_[joyStickAxes_[iDev][i].name_] = &(joyStickAxes_[iDev][i]);
238                allHalfAxes_.push_back(&(joyStickAxes_[iDev][i]));
239            }
240        }
241    }
242
243    /**
244    @brief
245        Loads the key and button bindings.
246    @return
247        True if loading succeeded.
248    */
249    void KeyBinder::loadBindings(const std::string& filename)
250    {
251        COUT(3) << "KeyBinder: Loading key bindings..." << std::endl;
252
253        if (filename.empty())
254            return;
255
256        ConfigFileManager::getInstance().setFilename(this->configFile_, filename);
257
258        // Parse bindings and create the ConfigValueContainers if necessary
259        clearBindings();
260        for (std::map<std::string, Button*>::const_iterator it = allButtons_.begin(); it != allButtons_.end(); ++it)
261            it->second->readConfigValue(this->configFile_);
262
263        COUT(3) << "KeyBinder: Loading key bindings done." << std::endl;
264    }
265
266    bool KeyBinder::setBinding(const std::string& binding, const std::string& name, bool bTemporary)
267    {
268        std::map<std::string, Button*>::iterator it = allButtons_.find(name);
269        if (it != allButtons_.end())
270        {
271            if (bTemporary)
272                it->second->configContainer_->tset(binding);
273            else
274                it->second->configContainer_->set(binding);
275            it->second->configContainer_->getValue(&(it->second->bindingString_), it->second);
276            return true;
277        }
278        else
279        {
280            COUT(2) << "Could not find key/button/axis with name '" << name << "'." << std::endl;
281            return false;
282        }
283    }
284
285    /**
286    @brief
287        Overwrites all bindings with ""
288    */
289    void KeyBinder::clearBindings()
290    {
291        for (std::map<std::string, Button*>::const_iterator it = allButtons_.begin(); it != allButtons_.end(); ++it)
292            it->second->clear();
293
294        for (unsigned int i = 0; i < paramCommandBuffer_.size(); i++)
295            delete paramCommandBuffer_[i];
296        paramCommandBuffer_.clear();
297    }
298
299    void KeyBinder::resetJoyStickAxes()
300    {
301        for (unsigned int iDev = 0; iDev < numberOfJoySticks_; ++iDev)
302        {
303            for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
304            {
305                joyStickAxes_[iDev][i].absVal_ = 0.0f;
306                joyStickAxes_[iDev][i].relVal_ = 0.0f;
307            }
308        }
309    }
310
311    void KeyBinder::updateMouse(float dt)
312    {
313        if (bDeriveMouseInput_)
314        {
315            // only update when derivation dt has passed
316            if (deriveTime_ > derivePeriod_)
317            {
318                for (int i = 0; i < 2; i++)
319                {
320                    if (mouseRelative_[i] < 0)
321                    {
322                        mouseAxes_[2*i + 0].absVal_
323                            = -mouseRelative_[i] / deriveTime_ * 0.0005 * mouseSensitivityDerived_;
324                        mouseAxes_[2*i + 1].absVal_ = 0.0f;
325                    }
326                    else if (mouseRelative_[i] > 0)
327                    {
328                        mouseAxes_[2*i + 0].absVal_ = 0.0f;
329                        mouseAxes_[2*i + 1].absVal_
330                            =  mouseRelative_[i] / deriveTime_ * 0.0005 * mouseSensitivityDerived_;
331                    }
332                    else
333                    {
334                        mouseAxes_[2*i + 0].absVal_ = 0.0f;
335                        mouseAxes_[2*i + 1].absVal_ = 0.0f;
336                    }
337                    mouseRelative_[i] = 0;
338                    mouseAxes_[2*i + 0].hasChanged_ = true;
339                    mouseAxes_[2*i + 1].hasChanged_ = true;
340                }
341                deriveTime_ = 0.0f;
342            }
343            else
344                deriveTime_ += dt;
345        }
346
347        for (unsigned int i = 0; i < MouseAxisCode::numberOfAxes * 2; i++)
348        {
349            // Why dividing relative value by dt? The reason lies in the simple fact, that when you
350            // press a button that has relative movement, that value has to be multiplied by dt to be
351            // frame rate independent. This can easily (and only) be done in updateInput(float).
352            // Hence we need to divide by dt here for the mouse to compensate, because the relative
353            // move movements have nothing to do with dt.
354            if (dt != 0.0f)
355            {
356                // just ignore if dt == 0.0 because we have multiplied by 0.0 anyway..
357                mouseAxes_[i].relVal_ /= dt;
358            }
359
360            tickHalfAxis(mouseAxes_[i]);
361        }
362    }
363
364    void KeyBinder::updateJoyStick(float dt, unsigned int joyStick)
365    {
366        for (unsigned int i = 0; i < JoyStickAxisCode::numberOfAxes * 2; i++)
367        {
368            tickHalfAxis(joyStickAxes_[joyStick][i]);
369        }
370    }
371
372    void KeyBinder::tickHalfAxis(HalfAxis& halfAxis)
373    {
374        // button mode
375        // TODO: optimize out all the half axes that don't act as a button at the moment
376        if (halfAxis.hasChanged_)
377        {
378            if (!halfAxis.pressed_ && halfAxis.absVal_ > halfAxis.buttonThreshold_)
379            {
380                // key pressed event
381                halfAxis.pressed_ = true;
382                if (halfAxis.nCommands_[KeybindMode::OnPress])
383                    halfAxis.execute(KeybindMode::OnPress);
384            }
385            else if (halfAxis.pressed_ && halfAxis.absVal_ < halfAxis.buttonThreshold_)
386            {
387                // key released event
388                halfAxis.pressed_ = false;
389                if (halfAxis.nCommands_[KeybindMode::OnRelease])
390                    halfAxis.execute(KeybindMode::OnRelease);
391            }
392            halfAxis.hasChanged_ = false;
393        }
394
395        if (halfAxis.pressed_)
396        {
397            // key held event
398            if (halfAxis.nCommands_[KeybindMode::OnHold])
399                halfAxis.execute(KeybindMode::OnHold);
400        }
401
402        // these are the actually useful axis bindings for analog input
403        if (!bFilterAnalogNoise_ || halfAxis.relVal_ > analogThreshold_ || halfAxis.absVal_ > analogThreshold_)
404        {
405            halfAxis.execute();
406        }
407    }
408
409    /**
410    @brief
411        Event handler for the mouseMoved Event.
412    @param e
413        Mouse state information
414    */
415    void KeyBinder::mouseMoved(IntVector2 abs_, IntVector2 rel_, IntVector2 clippingSize)
416    {
417        // y axis of mouse input is inverted
418        int rel[] = { rel_.x, -rel_.y };
419
420        if (bDeriveMouseInput_)
421        {
422            mouseRelative_[0] += rel[0];
423            mouseRelative_[1] += rel[1];
424        }
425        else
426        {
427            for (int i = 0; i < 2; i++)
428            {
429                if (rel[i]) // performance opt. for the case that rel[i] == 0
430                {
431                    // write absolute values
432                    mouseAxes_[2*i + 0].hasChanged_ = true;
433                    mouseAxes_[2*i + 1].hasChanged_ = true;
434                    mousePosition_[i] += rel[i];
435
436                    // clip absolute position
437                    if (mousePosition_[i] > mouseClippingSize_)
438                        mousePosition_[i] =  mouseClippingSize_;
439                    if (mousePosition_[i] < -mouseClippingSize_)
440                        mousePosition_[i] = -mouseClippingSize_;
441
442                    if (mousePosition_[i] < 0)
443                    {
444                        mouseAxes_[2*i + 0].absVal_ =  -mousePosition_[i]/(float)mouseClippingSize_ * mouseSensitivity_;
445                        mouseAxes_[2*i + 1].absVal_ =  0.0f;
446                    }
447                    else
448                    {
449                        mouseAxes_[2*i + 0].absVal_ =  0.0f;
450                        mouseAxes_[2*i + 1].absVal_ =   mousePosition_[i]/(float)mouseClippingSize_ * mouseSensitivity_;
451                    }
452                }
453            }
454        }
455
456        // relative
457        for (int i = 0; i < 2; i++)
458        {
459            if (rel[i] < 0)
460                mouseAxes_[0 + 2*i].relVal_ = -((float)rel[i])/(float)mouseClippingSize_ * mouseSensitivity_;
461            else
462                mouseAxes_[1 + 2*i].relVal_ =  ((float)rel[i])/(float)mouseClippingSize_ * mouseSensitivity_;
463        }
464    }
465
466    /**
467    @brief Event handler for the mouseScrolled Event.
468    @param e Mouse state information
469    */
470    void KeyBinder::mouseScrolled(int abs, int rel)
471    {
472        if (rel < 0)
473            for (int i = 0; i < -rel/mouseWheelStepSize_; i++)
474                mouseButtons_[8].execute(KeybindMode::OnPress, ((float)abs)/mouseWheelStepSize_);
475        else
476            for (int i = 0; i < rel/mouseWheelStepSize_; i++)
477                mouseButtons_[9].execute(KeybindMode::OnPress, ((float)abs)/mouseWheelStepSize_);
478    }
479
480    void KeyBinder::joyStickAxisMoved(unsigned int joyStickID, unsigned int axis, float value)
481    {
482        int i = axis * 2;
483        if (value < 0)
484        {
485            joyStickAxes_[joyStickID][i].absVal_ = -value;
486            joyStickAxes_[joyStickID][i].relVal_ = -value;
487            joyStickAxes_[joyStickID][i].hasChanged_ = true;
488            if (joyStickAxes_[joyStickID][i + 1].absVal_ > 0.0f)
489            {
490                joyStickAxes_[joyStickID][i + 1].absVal_ = -0.0f;
491                joyStickAxes_[joyStickID][i + 1].relVal_ = -0.0f;
492                joyStickAxes_[joyStickID][i + 1].hasChanged_ = true;
493            }
494        }
495        else
496        {
497            joyStickAxes_[joyStickID][i + 1].absVal_ = value;
498            joyStickAxes_[joyStickID][i + 1].relVal_ = value;
499            joyStickAxes_[joyStickID][i + 1].hasChanged_ = true;
500            if (joyStickAxes_[joyStickID][i].absVal_ > 0.0f)
501            {
502                joyStickAxes_[joyStickID][i].absVal_ = -0.0f;
503                joyStickAxes_[joyStickID][i].relVal_ = -0.0f;
504                joyStickAxes_[joyStickID][i].hasChanged_ = true;
505            }
506        }
507    }
508}
Note: See TracBrowser for help on using the repository browser.