Main Page Namespace List Class Hierarchy Alphabetical List Compound List File List Namespace Members Compound Members File Members Related Pages

# OgreInput.cpp

Go to the documentation of this file.

    00001 /*
    00002 -----------------------------------------------------------------------------
    00003 This source file is part of OGRE
    00004     (Object-oriented Graphics Rendering Engine)
    00005 For the latest info, see http://www.ogre3d.org/
    00006
    00007 Copyright © 2000-2002 The OGRE Team
    00008 Also see acknowledgements in Readme.html
    00009
    00010 This program is free software; you can redistribute it and/or modify it under
    00011 the terms of the GNU Lesser General Public License as published by the Free Software
    00012 Foundation; either version 2 of the License, or (at your option) any later
    00013 version.
    00014
    00015 This program is distributed in the hope that it will be useful, but WITHOUT
    00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
    00018
    00019 You should have received a copy of the GNU Lesser General Public License along with
    00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
    00021 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
    00022 http://www.gnu.org/copyleft/lesser.txt.
    00023 -----------------------------------------------------------------------------
    00024 */
    00025 #include "OgreStableHeaders.h"
    00026
    00027 #include "OgreInput.h"
    00028 #include "OgreCursor.h"
    00029 #include "OgreEventQueue.h"
    00030 #include "OgreKeyEvent.h"
    00031 #include "OgreEventListeners.h"
    00032 #include "OgreLogManager.h"
    00033
    00034
    00035 namespace Ogre {
    00036     static KeyChars sKeyChars;
    00037     bool InputReader::sKeysInitialised = false;
    00038     //-----------------------------------------------------------------------
    00039     InputReader::InputReader()
    00040     {
    00041         mCursor = 0;
    00042         mModifiers = 0;
    00043         mEventQueue = 0;
    00044         mUseBufferedKeys = false;
    00045         mUseBufferedMouse = false;
    00046         if (!sKeysInitialised)
    00047         {
    00048             setupKeyChars();
    00049             sKeysInitialised = true;
    00050         }
    00051     }
    00052
    00053     //-----------------------------------------------------------------------
    00054     void InputReader::useBufferedInput(EventQueue* pEventQueue, bool keys, bool mouse)
    00055     {
    00056         mEventQueue = pEventQueue;
    00057
    00058         if (mCursor)
    00059             delete mCursor;
    00060
    00061         mCursor = new Cursor();
    00062
    00063         // initial states of buffered don't call setBufferedInput
    00064         // because that can be overriden (in the future) to save releasing and acquiring unchanged inputs
    00065         // if we ever decide to release and acquire devices
    00066         mUseBufferedKeys = keys;
    00067         mUseBufferedMouse = mouse;
    00068     }
    00069
    00070     //-----------------------------------------------------------------------
    00071     void InputReader::setBufferedInput(bool keys, bool mouse)
    00072     {
    00073         mUseBufferedKeys = keys;
    00074         mUseBufferedMouse = mouse;
    00075     }
    00076
    00077
    00078     //-----------------------------------------------------------------------
    00079     InputReader::~InputReader()
    00080     {
    00081         if (mCursor)
    00082         {
    00083             delete mCursor;
    00084         }
    00085     }
    00086
    00087     //-----------------------------------------------------------------------
    00088     void InputReader::triggerMouseButton(int nMouseCode, bool mousePressed)
    00089     {
    00090         if (mousePressed)
    00091         {
    00092             mModifiers |= nMouseCode;
    00093             createMouseEvent(MouseEvent::ME_MOUSE_PRESSED, nMouseCode);
    00094             // Update immediate-mode mouse button state
    00095             switch(nMouseCode)
    00096             {
    00097             case InputEvent::BUTTON0_MASK:
    00098                 mMouseState.Buttons |= 0x1;
    00099                 break;
    00100             case InputEvent::BUTTON1_MASK:
    00101                 mMouseState.Buttons |= 0x2;
    00102                 break;
    00103             case InputEvent::BUTTON2_MASK:
    00104                 mMouseState.Buttons |= 0x4;
    00105                 break;
    00106             }
    00107
    00108         }
    00109         else
    00110         {   // button up... trigger MouseReleased, and MouseClicked
    00111             mModifiers &= !nMouseCode;
    00112             createMouseEvent(MouseEvent::ME_MOUSE_RELEASED, nMouseCode);
    00113             //createMouseEvent(MouseEvent::ME_MOUSE_CLICKED, nMouseCode);   JCA - moved to EventDispatcher
    00114             // Update immediate-mode mouse button state
    00115             switch(nMouseCode)
    00116             {
    00117             case InputEvent::BUTTON0_MASK:
    00118                 mMouseState.Buttons &= 0xFE;
    00119                 break;
    00120             case InputEvent::BUTTON1_MASK:
    00121                 mMouseState.Buttons &= 0xFD;
    00122                 break;
    00123             case InputEvent::BUTTON2_MASK:
    00124                 mMouseState.Buttons &= 0xFB;
    00125                 break;
    00126             }
    00127         }
    00128
    00129     }
    00130
    00131     //-----------------------------------------------------------------------
    00132     void InputReader::createMouseEvent(int id, int button)
    00133     {
    00134         MouseEvent* me =
    00135             new MouseEvent(
    00136                 NULL, id, button, 0, // hack fix time
    00137                 mModifiers,
    00138                 mCursor->getX(), mCursor->getY(), mCursor->getZ(),
    00139                 mCursor->getRelX(), mCursor->getRelY(), mCursor->getRelZ(),
    00140                 0
    00141             );  // hack fix click count
    00142
    00143
    00144         mCursor->processEvent(me);
    00145         mEventQueue->push(me);
    00146
    00147     }
    00148
    00149
    00150
    00151     //-----------------------------------------------------------------------
    00152     void InputReader::createKeyEvent(int id, int key)
    00153     {
    00154         KeyEvent* ke = new KeyEvent(NULL, id, key, 0, // hack fix time
    00155             mModifiers);    // hack fix click count
    00156         mEventQueue->push(ke);
    00157
    00158     }
    00159
    00160     //-----------------------------------------------------------------------
    00161     void InputReader::mouseMoved()
    00162     {
    00163
    00164         if (mModifiers & InputEvent::BUTTON_ANY_MASK)   // don't need to know which button. you can get that from the modifiers
    00165         {
    00166             createMouseEvent(MouseEvent::ME_MOUSE_DRAGGED, 0);
    00167         }
    00168         else
    00169         {
    00170             createMouseEvent(MouseEvent::ME_MOUSE_MOVED, 0);
    00171         }
    00172     }
    00173     //-----------------------------------------------------------------------
    00174     void InputReader::addCursorMoveListener(MouseMotionListener* c)
    00175     {
    00176         mCursor->addMouseMotionListener(c);
    00177     }
    00178     //-----------------------------------------------------------------------
    00179     void InputReader::removeCursorMoveListener(MouseMotionListener* c)
    00180     {
    00181         mCursor->removeMouseMotionListener(c);
    00182     }
    00183     //-----------------------------------------------------------------------
    00184
    00185     void InputReader::keyChanged(int key, bool down)
    00186     {
    00187         if (down)
    00188         {
    00189             switch (key) {
    00190             case KC_LMENU :
    00191             case KC_RMENU :
    00192                 mModifiers |= InputEvent::ALT_MASK;
    00193                 break;
    00194
    00195             case KC_LSHIFT :
    00196             case KC_RSHIFT :
    00197                 mModifiers |= InputEvent::SHIFT_MASK;
    00198                 break;
    00199
    00200             case KC_LCONTROL :
    00201             case KC_RCONTROL :
    00202                 mModifiers |= InputEvent::CTRL_MASK;
    00203                 break;
    00204             }
    00205
    00206             createKeyEvent(KeyEvent::KE_KEY_PRESSED, key);
    00207
    00208             // Update keydown map
    00209             mBufferedKeysDown.insert(static_cast<KeyCode>(key));
    00210         }
    00211         else
    00212         {
    00213             switch (key) {
    00214             case KC_LMENU :
    00215             case KC_RMENU :
    00216                 mModifiers &= ~InputEvent::ALT_MASK;
    00217                 break;
    00218
    00219             case KC_LSHIFT :
    00220             case KC_RSHIFT :
    00221                 mModifiers &= ~InputEvent::SHIFT_MASK;
    00222                 break;
    00223
    00224             case KC_LCONTROL :
    00225             case KC_RCONTROL :
    00226                 mModifiers &= ~InputEvent::CTRL_MASK;
    00227                 break;
    00228             }
    00229
    00230             createKeyEvent(KeyEvent::KE_KEY_RELEASED, key);
    00231             createKeyEvent(KeyEvent::KE_KEY_CLICKED, key);
    00232             // Update keydown map
    00233             mBufferedKeysDown.erase(static_cast<KeyCode>(key));
    00234         }
    00235     }
    00236
    00237
    00238     char InputReader::getKeyChar(int keyCode, long modifiers)
    00239     {
    00240         char c = 0;
    00241
    00242         KeyChars::iterator i = sKeyChars.find(KEYCODE(keyCode, modifiers));
    00243         if (i != sKeyChars.end())
    00244             c = i->second;
    00245         return c;
    00246     }
    00247
    00248     void InputReader::setupKeyChars()
    00249     {
    00250         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_1),              '1') );
    00251         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_2),              '2') );
    00252         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_3),              '3') );
    00253         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_4),              '4') );
    00254         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_5),              '5') );
    00255         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_6),              '6') );
    00256         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_7),              '7') );
    00257         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_8),              '8') );
    00258         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_9),              '9') );
    00259         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_0),              '0') );
    00260         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_MINUS),          '-') );/* - on main keyboard */
    00261         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_EQUALS),         '=') );
    00262         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_Q),              'q') );
    00263         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_W),              'w') );
    00264         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_E),              'e') );
    00265         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_R),              'r') );
    00266         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_T),              't') );
    00267         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_Y),              'y') );
    00268         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_U),              'u') );
    00269         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_I),              'i') );
    00270         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_O),              'o') );
    00271         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_P),              'p') );
    00272         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_LBRACKET),       '[') );
    00273         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_RBRACKET),       ']') );
    00274         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_A),              'a') );
    00275         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_S),              's') );
    00276         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_D),              'd') );
    00277         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_F),              'f') );
    00278         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_G),              'g') );
    00279         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_H),              'h') );
    00280         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_J),              'j') );
    00281         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_K),              'k') );
    00282         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_L),              'l') );
    00283         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_SEMICOLON),      ';') );
    00284         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_APOSTROPHE),     '\'') );
    00285         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_GRAVE),          '`') );  /* accent grave */
    00286         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_BACKSLASH),      '\\') );
    00287         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_Z),              'z') );
    00288         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_X),              'x') );
    00289         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_C),              'c') );
    00290         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_V),              'v') );
    00291         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_B),              'b') );
    00292         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_N),              'n') );
    00293         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_M),              'm') );
    00294         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_COMMA),          ',') );
    00295         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_PERIOD),         '.') );  /* . on main keyboard */
    00296         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_SLASH),          '/') );  /* '/' on main keyboard */
    00297         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_MULTIPLY),       '*') );  /* * on numeric keypad */
    00298         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_SPACE),          ' ') );
    00299         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD7),        '7') );
    00300         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD8),        '8') );
    00301         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD9),        '9') );
    00302         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_SUBTRACT),       '-') );  /* - on numeric keypad */
    00303         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD4),        '4') );
    00304         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD5),        '5') );
    00305         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD6),        '6') );
    00306         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_ADD),            '+') );  /* + on numeric keypad */
    00307         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD1),        '1') );
    00308         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD2),        '2') );
    00309         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD3),        '3') );
    00310         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPAD0),        '0') );
    00311         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_DECIMAL),        '.') );  /* . on numeric keypad */
    00312         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPADEQUALS),   '=') );  /* = on numeric keypad (NEC PC98) */
    00313         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_AT),             '@') );  /*                     (NEC PC98) */
    00314         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_COLON),          ':') );  /*                     (NEC PC98) */
    00315         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_UNDERLINE),      '_') );  /*                     (NEC PC98) */
    00316         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_NUMPADCOMMA),    ',') );  /* , on numeric keypad (NEC PC98) */
    00317         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_DIVIDE),         '/') );  /* / on numeric keypad */
    00318
    00319         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_1,            InputEvent::SHIFT_MASK), '!') );
    00320         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_2,            InputEvent::SHIFT_MASK), '@') );
    00321         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_3,            InputEvent::SHIFT_MASK), '#') );
    00322         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_4,            InputEvent::SHIFT_MASK), '$') );
    00323         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_5,            InputEvent::SHIFT_MASK), '%') );
    00324         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_6,            InputEvent::SHIFT_MASK), '^') );
    00325         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_7,            InputEvent::SHIFT_MASK), '&') );
    00326         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_8,            InputEvent::SHIFT_MASK), '*') );
    00327         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_9,            InputEvent::SHIFT_MASK), '(') );
    00328         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_0,            InputEvent::SHIFT_MASK), ')') );
    00329         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_MINUS,        InputEvent::SHIFT_MASK), '_') );
    00330         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_EQUALS,       InputEvent::SHIFT_MASK), '+') );
    00331         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_Q,            InputEvent::SHIFT_MASK), 'Q') );
    00332         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_W,            InputEvent::SHIFT_MASK), 'W') );
    00333         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_E,            InputEvent::SHIFT_MASK), 'E') );
    00334         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_R,            InputEvent::SHIFT_MASK), 'R') );
    00335         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_T,            InputEvent::SHIFT_MASK), 'T') );
    00336         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_Y,            InputEvent::SHIFT_MASK), 'Y') );
    00337         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_U,            InputEvent::SHIFT_MASK), 'U') );
    00338         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_I,            InputEvent::SHIFT_MASK), 'I') );
    00339         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_O,            InputEvent::SHIFT_MASK), 'O') );
    00340         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_P,            InputEvent::SHIFT_MASK), 'P') );
    00341         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_LBRACKET,     InputEvent::SHIFT_MASK), '{') );
    00342         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_RBRACKET,     InputEvent::SHIFT_MASK), '}') );
    00343         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_A,            InputEvent::SHIFT_MASK), 'A') );
    00344         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_S,            InputEvent::SHIFT_MASK), 'S') );
    00345         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_D,            InputEvent::SHIFT_MASK), 'D') );
    00346         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_F,            InputEvent::SHIFT_MASK), 'F') );
    00347         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_G,            InputEvent::SHIFT_MASK), 'G') );
    00348         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_H,            InputEvent::SHIFT_MASK), 'H') );
    00349         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_J,            InputEvent::SHIFT_MASK), 'J') );
    00350         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_K,            InputEvent::SHIFT_MASK), 'K') );
    00351         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_L,            InputEvent::SHIFT_MASK), 'L') );
    00352         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_SEMICOLON,    InputEvent::SHIFT_MASK), ':') );
    00353         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_APOSTROPHE,   InputEvent::SHIFT_MASK), '"') );
    00354         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_GRAVE,        InputEvent::SHIFT_MASK), '~') );  /* accent grave */
    00355         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_BACKSLASH,    InputEvent::SHIFT_MASK), '|') );
    00356         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_Z,            InputEvent::SHIFT_MASK), 'Z') );
    00357         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_X,            InputEvent::SHIFT_MASK), 'X') );
    00358         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_C,            InputEvent::SHIFT_MASK), 'C') );
    00359         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_V,            InputEvent::SHIFT_MASK), 'V') );
    00360         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_B,            InputEvent::SHIFT_MASK), 'B') );
    00361         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_N,            InputEvent::SHIFT_MASK), 'N') );
    00362         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_M,            InputEvent::SHIFT_MASK), 'M') );
    00363         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_COMMA,        InputEvent::SHIFT_MASK), '<') );
    00364         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_PERIOD,       InputEvent::SHIFT_MASK), '>') );  /* . on main keyboard */
    00365         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_SLASH,        InputEvent::SHIFT_MASK), '?') );  /* '/' on main keyboard */
    00366         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_MULTIPLY,     InputEvent::SHIFT_MASK), '*') );  /* * on numeric keypad */
    00367         sKeyChars.insert( KeyChars::value_type( KEYCODE(KC_SPACE,        InputEvent::SHIFT_MASK), ' ') );
    00368     }
    00369     //-----------------------------------------------------------------------
    00370     bool InputReader::isKeyDown( KeyCode kc ) const
    00371     {
    00372         if (mUseBufferedKeys)
    00373         {
    00374             return mBufferedKeysDown.find(kc) != mBufferedKeysDown.end();
    00375         }
    00376         else
    00377         {
    00378             return isKeyDownImmediate(kc);
    00379         }
    00380     }
    00381 }

Copyright © 2002-2003 by The OGRE Team
Last modified Sun Nov 28 19:48:30 2004