Framework Avanzado de Ogre Un Framework para un joven programador Avanzado


Introducción


Érase una vez, un joven programador que exploraba el mundo de los gráficos 3D, comenzó buscando un poderoso mago que lo ayudase. Por largo tiempo cruzó el país de punta a punta, hasta que finalmente se dió cuenta de que nunca alcanzaría este sueño.

Mientras estaba sentado ahí en la hierba, discutiendo consigo mismo, un feo, y verde Ogro vino hacia él. Tenía buen temperamento y silvaba una canción. Asi que cuando vió al pobre programador. Le pregunto que que le había hecho el mundo y el chico le contó la historia. Pero en vez de estar también enfadado...

"Chico, este es tu día. Encontré al chico perfecto! No soy como el mago que soñastes que te ayudaría, pero soy de algún modo poderoso. Puedo renderizarte todo y ensañarte todos los secretos de los gráficos 3D! Chico, vamos! No es bueno esperar aquí cuando tienes un mundo entero frente a ti!"

El Ogro enseñó al joven todo lo que sabíaa, comenzando con los tutoriales básicos primero y después incluso dejando el maligno ExampleApplication.h. Pero en ese punto, esto no era bastante. Él quería más:

  • Un sistema de Estado de Juego
  • Una interfaz gráfica de usuario
  • Diferentes modos de entrada
  • Carga de escenas y
  • Manipulación manual de materiales

Afortunadamente miró en la cara del Ogre esperando ver que le diría, ..., le dijo:

"Conozco el libro secreto perfecto para ti. Con todos tus deseos... se llama: El Framework Avanzado de Ogre!"

Un ejemplo que funciona directamente (sin compilación) puede descargarse aquí desde mi espacio web. Viene con el código, el media necesitado y también el archivo de solución de VisualStudio de Microsoft.
Si este archivo no estuviera disponible mas (porque reestructuré mi sitio web), sólo envía un mensaje al foro).

Si encuentras problemas mientras estas leyendo, también haz la pregunta oportuna en el foro.
Nota: Una versión antigua del framework hecha por un usuario de Ogre 1.7.1 y CEGUI 0.7.1 puede ser encontrada aquí.

Framework Avanzado de Ogre - Estado de Menu
external image dl1939&display&x=150&y=107
Framework Avanzado de Ogre - Estado de Juego
external image dl1938&display&x=150&y=107

Arquitectura


Bien, la arquitetura parece compleja, pero no es tan dura de seguir. Intentare explicar la mayoria y con un poco de paciencia, cada uno sabra su camino.

El proyecto entero consiste en 17 archivos de cabecera y fuentes, pero: No te asustes por este gran numero! Uno de estos archivos es el main.cpp, que es siempre mas complejo que los otros como son solo dos archivos de RapidXML y DotSceneLoader. Esto hace que falten 14 mas.

Otra pareja basica es la parte de la inicializacion basica de Ogre, similar al BasicOgreFramework.hpp / .cpp, con nada nuevo en ello y otra pareja llamada DemoApp.hpp / .cpp que es el punto central de organizacion con solo unas pocas lineas de codigo.

La parte mayor y mas compleja son los restantes 10 archivos que forman juntos el sistema del estado de la aplicacion. Pero ya que hay tres estados diferentes de aplicacion en el framework de demo, muchas partes de codigo son redundandes y solo copiaremos de un estado a otro. Asi que la cantidad actual de "lineas de codigo unicas es comprensible" no es tan grande!

Sistema del estado de la aplicación


Para el sistema del estado de la aplicacion, he usado una variacion como la mostrada aqui, con algunos cambios. Generalmente, el sistema entero funciona como esto:

Para cada estado de la aplicacion que queremos usar en nuestro programa, creamos una clase propia que herede de AppState.hpp. Asi cada estado de aplicacion tiene las mismas funciones basicas como entrar, pausar, resumir y actualizar y tambien su propia Camara y SceneManager.

La segunda parte importante de este sistema es el AppStateManager.hpp. Su tarea es manejar todos los estados del juego y conectarlos. Para hacer esto, el manager tiene una pila de estados activos y siempre ejecuta uno en lo alto de esta pila. En cualquier momento, puedes poner otro estado en la pila que sera usado hasta que sea sacado de la pila. En este caso, el manager resume ejecutando el estado que fue el que se elimino. Tan pronto como no ya estados activos, el manager cierra la aplicacion entera.

main.cpp


Comencemos con lo mas simple: el main.cpp. Nada sorprendente hasta ahora.
 
Todo lo que he hecho es crear una instancia de nuestra clase DemoApp y llamar a su funcion startDemo(). Eso es todo.

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "DemoApp.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
       DemoApp demo;
       try
       {
           demo.startDemo();
       }
       catch(std::exception& e)
       {
 
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
       MessageBoxA(NULL, e.what(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
       fprintf(stderr, "An exception has occurred: %s\n", e.what());
#endif
    }
 
    return 0;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||


DotSceneLoader


Como se menciono antes, no he mostrado este codigo aqui, como esta listo en la pagina RapidXML y DotSceneLoader. Solo copio estos dos archivos desde aqui:

  • DotSceneLoader.hpp
  • DotSceneLoader.cpp

Para hacer que funcionen necesitan tambien anyadir incluir rapidxml.hpp (mas informacion en la pagina DotSceneLoader).

AdvancedOgreFramework.hpp


Esta clase del OgreFramework es equivalente a la que se llama igual en El framework basico de Ogre. tiene las funciones necesarias para encender Ogre y ofrece un entorno para hacer pantallazos y usar Superposiciones en Depuracion. Contiene la mayoria de las variables de Ogre:

  • Root
  • RenderWindow
  • Viewport
  • Log
  • Timer
  • InputManager / Keyboard / Mouse
  • SDKTrays Manager

Tambien ofrece funciones para manejar la entrada , pero que no se usa aqui, como cada estado de aplicacion necesita una entrada diferente y por lo tanto es directamente modelado en los estados de aplicacion. Sin embargo, toda la parte comun de la aplicacion puede ser puesto aqui.

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#ifndef OGRE_FRAMEWORK_HPP
#define OGRE_FRAMEWORK_HPP
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include <OgreCamera.h>
#include <OgreEntity.h>
#include <OgreLogManager.h>
#include <OgreOverlay.h>
#include <OgreOverlayElement.h>
#include <OgreOverlayManager.h>
#include <OgreRoot.h>
#include <OgreViewport.h>
#include <OgreSceneManager.h>
#include <OgreRenderWindow.h>
#include <OgreConfigFile.h>
 
#include <OISEvents.h>
#include <OISInputManager.h>
#include <OISKeyboard.h>
#include <OISMouse.h>
 
#include <SdkTrays.h>
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class OgreFramework : public Ogre::Singleton<OgreFramework>, OIS::KeyListener, OIS::MouseListener
{
public:
        OgreFramework();
        ~OgreFramework();
 
        bool initOgre(Ogre::String wndTitle, OIS::KeyListener *pKeyListener = 0, OIS::MouseListener *pMouseListener = 0);
        void updateOgre(double timeSinceLastFrame);
 
        bool keyPressed(const OIS::KeyEvent &keyEventRef);
        bool keyReleased(const OIS::KeyEvent &keyEventRef);
 
        bool mouseMoved(const OIS::MouseEvent &evt);
        bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
        bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
 
        Ogre::Root* m_pRoot;
        Ogre::RenderWindow* m_pRenderWnd;
        Ogre::Viewport* m_pViewport;
        Ogre::Log* m_pLog;
        Ogre::Timer* m_pTimer;
 
        OIS::InputManager* m_pInputMgr;
        OIS::Keyboard* m_pKeyboard;
        OIS::Mouse* m_pMouse;
 
        OgreBites::SdkTrayManager* m_pTrayMgr;
 
private:
        OgreFramework(const OgreFramework&);
        OgreFramework& operator= (const OgreFramework&);
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
// |||||||||||||||||||||||||||||||||||||||||||||||


AdvancedOgreFramework.cpp


  • Primera línea: El Singleton es inicializado
  • OgreFramework(): Constructor

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AdvancedOgreFramework.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
using namespace Ogre;
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
template<> OgreFramework* Ogre::Singleton<OgreFramework>::ms_Singleton = 0;
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
OgreFramework::OgreFramework()
{
    m_pRoot = 0;
    m_pRenderWnd = 0;
    m_pViewport = 0;
    m_pLog = 0;
    m_pTimer = 0;
 
    m_pInputMgr = 0;
    m_pKeyboard = 0;
    m_pMouse = 0;
    m_pTrayMgr = 0;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||


  • ~OgreFramework(): Destructor, limpieza

OgreFramework::~OgreFramework()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Shutdown OGRE...");
    if(m_pTrayMgr) delete m_pTrayMgr;
    if(m_pInputMgr) OIS::InputManager::destroyInputSystem(m_pInputMgr);
    if(m_pRoot) delete m_pRoot;
}
 



  • initOgre(): Enciende Ogre con los siguientes pasos:

  1. crea un manager de log
  2. crea un Root
  3. crea una RenderWindow y el Viewport
  4. enciende OIS
  5. si no fue pasado como parametro MouseListener o KeyboardListener, usa uno de esta clase, llamando a las funciones de la clase de input del OgreFramework cuando quieras manejar la entrada)
  6. carga recursos
  7. comienza el Timer
  8. establece el SDKTrayManager
  9. crea y muestra la superposicion de depuracion

Nota: despues de ejecutar esta funcion todavia no has visto nada en la pantalla ya que no hay camera y no hay SceneManager. Estos son miembros de los estados de aplicacion!

bool OgreFramework::initOgre(Ogre::String wndTitle, OIS::KeyListener *pKeyListener, OIS::MouseListener *pMouseListener)
{
     Ogre::LogManager* logMgr = new Ogre::LogManager();
 
     m_pLog = Ogre::LogManager::getSingleton().createLog("OgreLogfile.log", true, true, false);
 
     m_pLog->setDebugOutputEnabled(true);
     m_pRoot = new Ogre::Root();
 
     if(!m_pRoot->showConfigDialog())
          return false;
 
     m_pRenderWnd = m_pRoot->initialise(true, wndTitle);
 
     m_pViewport = m_pRenderWnd->addViewport(0);
     m_pViewport->setBackgroundColour(ColourValue(0.5f, 0.5f, 0.5f, 1.0f));
     m_pViewport->setCamera(0);
 
     unsigned long hWnd = 0;
     OIS::ParamList paramList;
     m_pRenderWnd->getCustomAttribute("WINDOW", &hWnd);
 
     paramList.insert(OIS::ParamList::value_type("WINDOW", Ogre::StringConverter::toString(hWnd)));
 
     m_pInputMgr = OIS::InputManager::createInputSystem(paramList);
 
     m_pKeyboard = static_cast<OIS::Keyboard*>(m_pInputMgr->createInputObject(OIS::OISKeyboard, true));
     m_pMouse = static_cast<OIS::Mouse*>(m_pInputMgr->createInputObject(OIS::OISMouse, true));
     m_pMouse->getMouseState().height = m_pRenderWnd->getHeight();
     m_pMouse->getMouseState().width = m_pRenderWnd->getWidth();
 
     if(pKeyListener == 0)
           m_pKeyboard->setEventCallback(this);
     else
           m_pKeyboard->setEventCallback(pKeyListener);
 
     if(pMouseListener == 0)
           m_pMouse->setEventCallback(this);
     else
           m_pMouse->setEventCallback(pMouseListener);
 
     Ogre::String secName, typeName, archName;
     Ogre::ConfigFile cf;
     cf.load("resources.cfg");
 
     Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
     while (seci.hasMoreElements())
     {
          secName = seci.peekNextKey();
          Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
 
          Ogre::ConfigFile::SettingsMultiMap::iterator i;
          for (i = settings->begin(); i != settings->end(); ++i)
          {
                typeName = i->first;
                archName = i->second;
                Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
          }
      }
      Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
 
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
 
m_pTrayMgr = new OgreBites::SdkTrayManager("AOFTrayMgr", m_pRenderWnd, m_pMouse, 0);
 
m_pTimer = new Ogre::Timer();
m_pTimer->reset();
 
m_pRenderWnd->setActive(true);
 
   return true;
}

  • keyPressed(): maneja la entrada con bufer, comun para toda la aplicacion
  • keyReleased(): lo mismo que antes
  • mouseMoved(): lo mismo que antes
  • mousePressed(): lo mismo que antes
  • mouseReleased(): lo mismo que antes

bool OgreFramework::keyPressed(const OIS::KeyEvent &keyEventRef)
{
     if(m_pKeyboard->isKeyDown(OIS::KC_SYSRQ))
     {
          m_pRenderWnd->writeContentsToTimestampedFile("AOF_Screenshot_", ".jpg");
          return true;
     }
 
     if(m_pKeyboard->isKeyDown(OIS::KC_O))
     {
          if(m_pTrayMgr->isLogoVisible())
          {
              m_pTrayMgr->hideFrameStats();
              m_pTrayMgr->hideLogo();
          }
          else
          {
              m_pTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
              m_pTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
          }
     }
 
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool OgreFramework::keyReleased(const OIS::KeyEvent &keyEventRef)
{
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool OgreFramework::mouseMoved(const OIS::MouseEvent &evt)
{
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool OgreFramework::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool OgreFramework::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     return true;
}


  • updateOgre(): Se llama una vez por frame por el AppStateManager para actulizar todo lo directamente relacionado con Ogre, en nuestro caso ... nada. En esta aplicacion todas las actualizaciones tienen en cuenta los estados de aplicacion, pero si hubiera alguna tarea central de Ogre, vendria aqui.

void OgreFramework::updateOgre(double timeSinceLastFrame)
{
}

AppState.hpp


En este archivo, se definen dos clases:

  • AppStateListener y
  • AppState

La primera clase sera mas tarde heredada por el manager del estado de aplicacion, pero tienes que definirlo aqui por razones de diseño.

  • AppstateListener(): Constructor
  • ~AppStateListner(): Destructor
  • manageGameState(): Función para añadir más tarde un estado al manager
  • changeAppState(): Sale del estado actual de la aplicacion y comienza uno especificado por el parametro.
  • pushAppState(): Pone un nuevo estado de aplicacion en la pila del estado activo que se ejecutara.
  • popGameState(): Elimina el estado activo superior de la pila, lo que resulta en volver al estado anterior
  • shutdown(): Bien, adivina que pasa...
  • popAllAndPushAppState(): Elimina todos los estados actuales de aplicacion de la pila y los mueve a un nuevo estado dado

La segunda clase es la marca del estado de aplicacion de cual el estado actual de la aplicacion hereda:

  • algunas funciones para entrar, salir, pausar, resumir y actualizar el estado
  • algunas funciones para llamar a otros estados (ordenes para manejar o para empezarlos)
  • un puntero al manager (lo cual es tambien un AppStateListener)
  • nuestra propia Camera y SceneManager

La última parte de este archivo es una gran sentencia de #define . Define la macro DECLARE_APPSTATE_CLASS mediante esto puedes más tarde crear los estados del juego.

Nota: Las barras invertidas son muy importantes para decirle al compilador que tome estas lineas como una sola. De otro modo de darian errores de compilacion...

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#ifndef APP_STATE_HPP
#define APP_STATE_HPP
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AdvancedOgreFramework.hpp"
 
class AppState;
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class AppStateListener
{
public:
        AppStateListener(){};
        virtual ~AppStateListener(){};
 
        virtual void manageAppState(Ogre::String stateName, AppState* state) = 0;
 
        virtual AppState* findByName(Ogre::String stateName) = 0;
 
        virtual void changeAppState(AppState *state) = 0;
        virtual bool pushAppState(AppState* state) = 0;
        virtual void popAppState() = 0;
        virtual void pauseAppState(AppState* state) = 0;
        virtual void shutdown() = 0;
        virtual void popAllAndPushAppState(AppState* state) = 0;
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class AppState : public OIS::KeyListener, public OIS::MouseListener, public OgreBites::SdkTrayListener
{
public:
        static void create(AppStateListener* parent, const Ogre::String name){};
 
        void destroy(){delete this;}
 
        virtual void enter() = 0;
        virtual void exit() = 0;
        virtual bool pause(){return true;}
        virtual void resume(){};
        virtual void update(double timeSinceLastFrame) = 0;
 
protected:
        AppState(){};
 
        AppState* findByName(Ogre::String stateName){return m_pParent->findByName(stateName);}
        void changeAppState(AppState* state){m_pParent->changeAppState(state);}
        bool pushAppState(AppState* state){return m_pParent->pushAppState(state);}
        void popAppState(){m_pParent->popAppState();}
        void shutdown(){m_pParent->shutdown();}
        void popAllAndPushAppState(AppState* state){m_pParent->popAllAndPushAppState(state);}
        AppStateListener* m_pParent;
 
        Ogre::Camera* m_pCamera;
        Ogre::SceneManager* m_pSceneMgr;
        Ogre::FrameEvent m_FrameEvent;
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#define DECLARE_APPSTATE_CLASS(T)
\
static void create(AppStateListener* parent, const Ogre::String name) \
{
\
        T* myAppState = new T();
\
        myAppState->m_pParent = parent;
\
        parent->manageAppState(name, myAppState);
\
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 


AppStateManager.hpp


La clase AppStateManager hereda de la clase AppStateListener mostrada antes y principalmente implementa sus metodos abstractos. Ademas contiene:

  • un std::vector para todos los estados existentes (m_States)
  • un std::vector para los estados activos, asi que la pila de estos estados es (m_ActiveStateStack)

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#ifndef APP_STATE_MANAGER_HPP
#define APP_STATE_MANAGER_HPP
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AppState.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class AppStateManager : public AppStateListener
{
public:
        typedef struct
        {
             Ogre::String name;
             AppState* state;
        } state_info;
 
        AppStateManager();
        ~AppStateManager();
 
        void manageAppState(Ogre::String stateName, AppState* state);
 
        AppState* findByName(Ogre::String stateName);
 
        void start(AppState* state);
        void changeAppState(AppState* state);
        bool pushAppState(AppState* state);
        void popAppState(void);
        void pauseAppState(AppState* state);
        void shutdown(void);
        void popAllAndPushAppState(AppState* state);
 
protected:
        void init(AppState *state);
 
        std::vector<AppState*> m_ActiveStateStack;
        std::vector<state_info> m_States;
        bool m_bShutdown;
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
// |||||||||||||||||||||||||||||||||||||||||||||||


AppStateManager.cpp


  • AppStateManager(): Constructor, sólo ajusta el indicador de apagado
  • ~AppStateManager(): Destructor, sale de todos los estados de aplicacion activos y vacia std::vectors

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AppStateManager.hpp"
 
#include <OgreWindowEventUtilities.h>
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
AppStateManager::AppStateManager()
{
       m_bShutdown = false;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
AppStateManager::~AppStateManager()
{
      state_info si;
 
      while(!m_ActiveStateStack.empty())
      {
           m_ActiveStateStack.back()->exit();
           m_ActiveStateStack.pop_back();
      }
 
      while(!m_States.empty())
      {
           si = m_States.back();
           si.state->destroy();
           m_States.pop_back();
      }
}

  • manageAppState(): llamado desde dentro del estado de creacion de macro y establece alguna informacion del nuevo estado, al igual que lo pone en la pila de estados activos.

void AppStateManager::manageAppState(Ogre::String stateName, AppState* state)
{
      try
      {
             state_info new_state_info;
             new_state_info.name = stateName;
             new_state_info.state = state;
             m_States.push_back(new_state_info);
      }
      catch(std::exception& e)
      {
             delete state;
             throw Ogre::Exception(Ogre::Exception::ERR_INTERNAL_ERROR, "Error while trying to manage a new AppState\n" + Ogre::String(e.what()), "AppStateManager.cpp (39)");
      }
}


  • findByName(): Devuelve un puntero al estado con el nombre respectivo

AppState* AppStateManager::findByName(Ogre::String stateName)
{
      std::vector<state_info>::iterator itr;
 
      for(itr=m_States.begin();itr!=m_States.end();itr++)
      {
            if(itr->name==stateName)
                 return itr->state;
      }
 
      return 0;
}


  • start(): bucle principal de la aplicacion que hace los siguientes pasos:
  1. cambia al estado especificado
  2. comienza el bucle
  3. captura la entrada de teclado y de raton
  4. actualiza el estado actual (el mas alto de la pila)
  5. llama a la clase OgreFramework para actualizar y renderizar

void AppStateManager::start(AppState* state)
{
        changeAppState(state);
 
        int timeSinceLastFrame = 1;
        int startTime = 0;
 
        while(!m_bShutdown)
        {
             if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isClosed())m_bShutdown = true;
 
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
 
Ogre::WindowEventUtilities::messagePump();
#endif
 
             if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isActive())
             {
                    startTime = OgreFramework::getSingletonPtr()->m_pTimer->getMillisecondsCPU();
 
                    OgreFramework::getSingletonPtr()->m_pKeyboard->capture();
                    OgreFramework::getSingletonPtr()->m_pMouse->capture();
                    m_ActiveStateStack.back()->update(timeSinceLastFrame);
                    OgreFramework::getSingletonPtr()->updateOgre(timeSinceLastFrame);
                    OgreFramework::getSingletonPtr()->m_pRoot->renderOneFrame();
 
                    timeSinceLastFrame = OgreFramework::getSingletonPtr()->m_pTimer->getMillisecondsCPU() - startTime;
             }
             else
             {
                    Sleep(1000);
             }
         }
 
         OgreFramework::getSingletonPtr()->m_pLog->logMessage("Main loop quit");
}


  • changeAppState(): Sale del estado actual (si hay alguno) y comienza el nuevo especificado.

void AppStateManager::changeAppState(AppState* state)
{
      if(!m_ActiveStateStack.empty())
      {
             m_ActiveStateStack.back()->exit();
             m_ActiveStateStack.pop_back();
      }
 
      m_ActiveStateStack.push_back(state);
      init(state);
      m_ActiveStateStack.back()->enter();
}


  • pushAppState(): Pone un nuevo estado en lo alto de la pila y lo inicia

bool AppStateManager::pushAppState(AppState* state)
{
     if(!m_ActiveStateStack.empty())
     {
           if(!m_ActiveStateStack.back()->pause())
                return false;
     }
 
     m_ActiveStateStack.push_back(state);
     init(state);
     m_ActiveStateStack.back()->enter();
 
     return true;
}

  • popAppState(): Elimina el estado superior y vuelve al anterior si habia uno, de otra forma sale de la aplicacion.

void AppStateManager::popAppState(void)
{
       if(!m_ActiveStateStack.empty())
       {
              m_ActiveStateStack.back()->exit();
              m_ActiveStateStack.pop_back();
       }
 
       if(!m_ActiveStateStack.empty())
       {
              init(m_ActiveStateStack.back());
              m_ActiveStateStack.back()->resume();
       }
   else
              shutdown();
}


  • popAllAndPushAppState(): Sale de todo estado de aplicacion existente de la pila y entre el nuevo estado.

void AppStateManager::popAllAndPushAppState(AppState* state)
{
     while(!m_ActiveStateStack.empty())
     {
          m_ActiveStateStack.back()->exit();
          m_ActiveStateStack.pop_back();
     }
 
     pushAppState(state);
}
  • pauseAppState(): Pausa el estado actual de la aplicación y vuelve a uno de la pila anterior

shutdown(): Sale de la aplicacion

void AppStateManager::shutdown()
{
    m_bShutdown = true;
}

  • init(): Inicializa un nuevo estado y enlaza la entrada y SDKTrays a el, reseteando las estadisticas de Ogre (FPS, cuenta de triangulos, cuenta de lotes, ...)

void AppStateManager::init(AppState* state)
{
     OgreFramework::getSingletonPtr()->m_pKeyboard->setEventCallback(state);
     OgreFramework::getSingletonPtr()->m_pMouse->setEventCallback(state);
     OgreFramework::getSingletonPtr()->m_pTrayMgr->setListener(state);
     OgreFramework::getSingletonPtr()->m_pRenderWnd->resetStatistics();
}

MenuState.hpp


La clase MenuState es una de las implementaciones de la clase AppState actual. Hereda de AppState para asegurar que todos los estados tienen las mismas funciones comunes como enter(), exit(), pause(), resume() o update().

  • DECLARE_APPSTATE_CLASS(MenuState): Esta linea llama a la macro definida en AppState.hpp, haciendo esta clase un estado valido de aplicacion

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#ifndef MENU_STATE_HPP
#define MENU_STATE_HPP
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AppState.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class MenuState : public AppState
{
public:
      MenuState();
 
         DECLARE_APPSTATE_CLASS(MenuState)
 
         void enter();
         void createScene();
         void exit();
 
         bool keyPressed(const OIS::KeyEvent &keyEventRef);
         bool keyReleased(const OIS::KeyEvent &keyEventRef);
 
         bool mouseMoved(const OIS::MouseEvent &evt);
         bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
         bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
 
         void buttonHit(OgreBites::Button* button);
 
         void update(double timeSinceLastFrame);
 
private:
         bool m_bQuit;
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
// |||||||||||||||||||||||||||||||||||||||||||||||


MenuState.cpp


  • enter(): Se llama siempre cuando comenzamos este estado y hace los siguientes pasos:

  1. crea un SceneManager
  2. crea una Camera
  3. establece las llamadas de la entrada
  4. compila GUI
  5. llama a createScene() para llenar la escena con contenido

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "MenuState.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
using namespace Ogre;
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
MenuState::MenuState()
{
    m_bQuit = false;
    m_FrameEvent = Ogre::FrameEvent();
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void MenuState::enter()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering MenuState...");
 
    m_pSceneMgr = OgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "MenuSceneMgr");
    m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.7f, 0.7f, 0.7f));
 
    m_pCamera = m_pSceneMgr->createCamera("MenuCam");
    m_pCamera->setPosition(Vector3(0, 25, -50));
    m_pCamera->lookAt(Vector3(0, 0, 0));
    m_pCamera->setNearClipDistance(1);
    m_pCamera->setAspectRatio(Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualWidth()) /
 
    Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualHeight()));
 
    OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->destroyAllWidgets();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->showCursor();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "EnterBtn", "Enter GameState", 250);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "ExitBtn", "Exit AdvancedOgreFramework", 250);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createLabel(OgreBites::TL_TOP, "MenuLbl", "Menu mode", 250);
 
    createScene();
}


  • createScene(): Llena la escena con contenido (no contenido en el MenuState de aqui)
  • exit(): Destruye la Camera y el SceneManager al igual que los elementos de GUI.

void MenuState::createScene()
{
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void MenuState::exit()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Leaving MenuState...");
 
    m_pSceneMgr->destroyCamera(m_pCamera);
    if(m_pSceneMgr)
         OgreFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr);
 
    OgreFramework::getSingletonPtr()->m_pTrayMgr->clearAllTrays();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->destroyAllWidgets();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->setListener(0);
}


Funciones de manejo de la entrada:

  • keyPressed(): Sale con Escape y si la entrada no manejado de la clase del OgreFramework
  • keyReleased(): Solo sigue a la clase OgreFramework
  • mouseMoved(): Inyecta los movimientos del raton en la GUI
  • mousePressed(): Inyecta las pulsaciones del raton en la GUI
  • mouseReleased(): Inyecta las pulsaciones del raton en la GUI

bool MenuState::keyPressed(const OIS::KeyEvent &keyEventRef)
{
     if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_ESCAPE))
     {
          m_bQuit = true;
          return true;
     }
 
     OgreFramework::getSingletonPtr()->keyPressed(keyEventRef);
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool MenuState::keyReleased(const OIS::KeyEvent &keyEventRef)
{
     OgreFramework::getSingletonPtr()->keyReleased(keyEventRef);
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool MenuState::mouseMoved(const OIS::MouseEvent &evt)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseMove(evt)) return true;
          return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool MenuState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseDown(evt, id)) return true;
          return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
bool MenuState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseUp(evt, id)) return true;
          return true;
}

  • update(): Updates the GUI and checks whether the state is to be quit.

void MenuState::update(double timeSinceLastFrame)
{
     m_FrameEvent.timeSinceLastFrame = timeSinceLastFrame;
     OgreFramework::getSingletonPtr()->m_pTrayMgr->frameRenderingQueued(m_FrameEvent);
 
     if(m_bQuit == true)
     {
         shutdown();
         return;
     }
}

  • buttonHit(): Función de llamada que se activa cuando se pulsa un botón.

void MenuState::buttonHit(OgreBites::Button *button)
{
if (button->getName() == "ExitBtn")
   m_bQuit = true;
else if(button->getName() == "EnterBtn")
    changeAppState(findByName("GameState"));
}


GameState.hpp


El segundo estado de aplicacion actual implementado, tambien hereda de AppState. Es similar al MenuState.hpp pero con algo mas en la pantalla e internamente.

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#ifndef GAME_STATE_HPP
#define GAME_STATE_HPP
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AppState.hpp"
 
#include "DotSceneLoader.hpp"
 
#include <OgreSubEntity.h>
#include <OgreMaterialManager.h>
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
enum QueryFlags
{
     OGRE_HEAD_MASK = 1<<0,
     CUBE_MASK = 1<<1
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class GameState : public AppState
{
public:
        GameState();
 
        DECLARE_APPSTATE_CLASS(GameState)
 
        void enter();
        void createScene();
        void exit();
        bool pause();
        void resume();
 
        void moveCamera();
        void getInput();
        void buildGUI();
 
        bool keyPressed(const OIS::KeyEvent &keyEventRef);
        bool keyReleased(const OIS::KeyEvent &keyEventRef);
 
        bool mouseMoved(const OIS::MouseEvent &arg);
        bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id);
        bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id);
 
        void onLeftPressed(const OIS::MouseEvent &evt);
        void itemSelected(OgreBites::SelectMenu* menu);
 
        void update(double timeSinceLastFrame);
 
private:
        Ogre::SceneNode* m_pOgreHeadNode;
        Ogre::Entity* m_pOgreHeadEntity;
        Ogre::MaterialPtr m_pOgreHeadMat;
        Ogre::MaterialPtr m_pOgreHeadMatHigh;
 
        OgreBites::ParamsPanel* m_pDetailsPanel;
        bool m_bQuit;
 
        Ogre::Vector3 m_TranslateVector;
        Ogre::Real m_MoveSpeed;
        Ogre::Degree m_RotateSpeed;
        float m_MoveScale;
        Ogre::Degree m_RotScale;
 
        Ogre::RaySceneQuery* m_pRSQ;
        Ogre::SceneNode* m_pCurrentObject;
        Ogre::Entity* m_pCurrentEntity;
        bool m_bLMouseDown, m_bRMouseDown;
        bool m_bSettingsMode;
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
// |||||||||||||||||||||||||||||||||||||||||||||||

GameState.cpp


  • GameState(): Constructor
  • enter(): Instalacion Basica de la escena y del GUI

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "GameState.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
using namespace Ogre; |||||||||||||||||||||||||||||||||||||||||||||||
 
GameState::GameState()
{
    m_MoveSpeed = 0.1f;
    m_RotateSpeed = 0.3f;
 
    m_bLMouseDown = false;
    m_bRMouseDown = false;
    m_bQuit = false;
    m_bSettingsMode = false;
    m_pCurrentObject = 0;
 
    m_pDetailsPanel = 0;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void GameState::enter()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering GameState...");
 
    m_pSceneMgr = OgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "GameSceneMgr");
    m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.7f, 0.7f, 0.7f));
 
    m_pRSQ = m_pSceneMgr->createRayQuery(Ray());
    m_pRSQ->setQueryMask(OGRE_HEAD_MASK);
 
    m_pCamera = m_pSceneMgr->createCamera("GameCamera");
    m_pCamera->setPosition(Vector3(5, 60, 60));
    m_pCamera->lookAt(Vector3(5, 20, 0));
    m_pCamera->setNearClipDistance(5);
 
    m_pCamera->setAspectRatio(Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualWidth()) /
    Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualHeight()));
 
    OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
 
    buildGUI();
 
    createScene();
}


  • pause(): LLamado por el manejador de estado de aplicacion cuando el estado del juego es pausado
  • resume(): Llamado por el manejador de estado de aplicacion cuano el estado del juego es resumido y recompilada la GUI y establece la camera activa.

bool GameState::pause()
{
     OgreFramework::getSingletonPtr()->m_pLog->logMessage("Pausing GameState...");
 
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void GameState::resume()
{
     OgreFramework::getSingletonPtr()->m_pLog->logMessage("Resuming GameState...");
 
     buildGUI();
 
     OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
     m_bQuit = false;
}


  • exit(): Simiar a la funcion pause(), pero tambien destruye la Camera, el SceneManager y el RaySceneQuery

void GameState::exit()
{
     OgreFramework::getSingletonPtr()->m_pLog->logMessage("Leaving GameState...");
 
     m_pSceneMgr->destroyCamera(m_pCamera);
     m_pSceneMgr->destroyQuery(m_pRSQ);
     if(m_pSceneMgr)
          OgreFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr);
}


  • createScene(): Llena la escena con contenido:

  1. crea una Luz
  2. instancia el DotSceneLoader
  3. carga un DotScene(CubeScene.xml) desde el grupo de recursos General
  4. devolviendo las Entities de el DotScene y establece el QueryMaskFlag (asi que ellos no tienen que seleccionarlo con el raton)
  5. pone la OgreHead(la cabeza del Ogro) en la escena y tambien estable un QueryFlag, asi que puede ser seleccionado con el raton
  6. consigue el material actual de la cabeza, lo clona y cambia el clonado a rojo

void GameState::createScene()
{
    m_pSceneMgr->createLight("Light")->setPosition(75,75,75);
 
    DotSceneLoader* pDotSceneLoader = new DotSceneLoader();
    pDotSceneLoader->parseDotScene("CubeScene.xml", "General", m_pSceneMgr, m_pSceneMgr->getRootSceneNode());
    delete pDotSceneLoader;
 
    m_pSceneMgr->getEntity("Cube01")->setQueryFlags(CUBE_MASK);
    m_pSceneMgr->getEntity("Cube02")->setQueryFlags(CUBE_MASK);
    m_pSceneMgr->getEntity("Cube03")->setQueryFlags(CUBE_MASK);
 
    m_pOgreHeadEntity = m_pSceneMgr->createEntity("Cube", "ogrehead.mesh");
    m_pOgreHeadEntity->setQueryFlags(OGRE_HEAD_MASK);
    m_pOgreHeadNode = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("CubeNode");
    m_pOgreHeadNode->attachObject(m_pOgreHeadEntity);
 
    m_pOgreHeadNode->setPosition(Vector3(0, 0, -25));
    m_pOgreHeadMat = m_pOgreHeadEntity->getSubEntity(1)->getMaterial();
    m_pOgreHeadMatHigh = m_pOgreHeadMat->clone("OgreHeadMatHigh");
    m_pOgreHeadMatHigh->getTechnique(0)->getPass(0)->setAmbient(1, 0, 0);
    m_pOgreHeadMatHigh->getTechnique(0)->getPass(0)->setDiffuse(1, 0, 0, 0);
}


  • keyPressed(): Maneja la entrada con bufer:

  1. Si estamos en modo de ajustes (podemos cambiar mediante [Tab]) entonces todas las pulsaciones de tecla son inyectadas cono deberian ser procesadas por el GUI
  2. Para comprobar si [Escape], [Tab] para cambiar los modos de entrada y [Return] / [Enter] cuando estamos en el modo de chat para enviar el texto tecleado a la caja de mensaje.
  3. Si a) no estamos en el modo chat o b) estamos en el modo chat, pero [O] no fue presionado, pondemos pasar la entrada no manajeda a la clase OgreFramework(sin esta comprobacion, ej: tecleando un [O] en chat mode deberia tambien pasarse al OgreFramework resultante en que tambien cambia las superposiciones para cada tecleado [O])
  • keyReleased(): Solo pasa la entrada a la clase OgreFramework

bool GameState::keyPressed(const OIS::KeyEvent &keyEventRef)
{
     if(m_bSettingsMode == true)
     {
         if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_S))
         {
              OgreBites::SelectMenu* pMenu = (OgreBites::SelectMenu*)OgreFramework::getSingletonPtr()->m_pTrayMgr->getWidget("ChatModeSelMenu");
              if(pMenu->getSelectionIndex() + 1 < (int)pMenu->getNumItems())
                    pMenu->selectItem(pMenu->getSelectionIndex() + 1);
         }
 
         if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_W))
         {
              OgreBites::SelectMenu* pMenu = (OgreBites::SelectMenu*)OgreFramework::getSingletonPtr()->m_pTrayMgr->getWidget("ChatModeSelMenu");
              if(pMenu->getSelectionIndex() - 1 >= 0)
              pMenu->selectItem(pMenu->getSelectionIndex() - 1);
         }
     }
 
     if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_ESCAPE))
     {
            pushAppState(findByName("PauseState"));
            return true;
     }
 
     if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_I))
     {
            if(m_pDetailsPanel->getTrayLocation() == OgreBites::TL_NONE)
            {
                  OgreFramework::getSingletonPtr()->m_pTrayMgr->moveWidgetToTray(m_pDetailsPanel, OgreBites::TL_TOPLEFT, 0);
                  m_pDetailsPanel->show();
            }
            else
            {
                  OgreFramework::getSingletonPtr()->m_pTrayMgr->removeWidgetFromTray(m_pDetailsPanel);
                  m_pDetailsPanel->hide();
            }
      }
 
      if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_TAB))
      {
             m_bSettingsMode = !m_bSettingsMode;
             return true;
      }
 
      if(m_bSettingsMode && OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_RETURN) ||
           OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_NUMPADENTER))
       {
       }
 
      if(!m_bSettingsMode || (m_bSettingsMode && !OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_O)))
           OgreFramework::getSingletonPtr()->keyPressed(keyEventRef);
      return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool GameState::keyReleased(const OIS::KeyEvent &keyEventRef)
{
       OgreFramework::getSingletonPtr()->keyPressed(keyEventRef);
       return true;
}

  • mouseMove(): Maneja los movimientos del raton:
  1. inyecta el movimiento a la GUI
  2. si el boton derecho de raton es presionado, manipula la camara.

  • mousePressed(): Comprueba que boton fue presionado y establece el values boleano interno al igual que inyecta el click izquierdo a la GUI y llama a la funcion onLeftPressed() para la seleccion del objeto con el raton.
  • mouseReleased(): Lo mismo que antes, pero para la tecla soltada

bool GameState::mouseMoved(const OIS::MouseEvent &evt)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseMove(evt)) return true;
 
     if(m_bRMouseDown)
     {
          m_pCamera->yaw(Degree(evt.state.X.rel * -0.1f));
          m_pCamera->pitch(Degree(evt.state.Y.rel * -0.1f));
     }
 
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool GameState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseDown(evt, id)) return true;
 
     if(id == OIS::MB_Left)
     {
          onLeftPressed(evt);
          m_bLMouseDown = true;
     }
     else if(id == OIS::MB_Right)
     {
          m_bRMouseDown = true;
     }
 
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool GameState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseUp(evt, id)) return true;
 
     if(id == OIS::MB_Left)
     {
         m_bLMouseDown = false;
     }
     else if(id == OIS::MB_Right)
     {
         m_bRMouseDown = false;
     }
return true;
}

  • onLeftPressed(): Seleccion del objeto:

  1. Si hay un objeto seleccionado (que deberia ser guardado en m_pCurrentObject) oculta su caja limite y establece otro material para la cabeza de Ogre (la cabeza de Ogre es solo un objeto seleccionable en nuestra escena debido a la QueryMask)
  2. consigue la posicion de nuestro cursor del raton
  3. crea un Rayo desde la camera a esta posicion y comprueba por pulsaciones en cada iteracion.
  4. Itera por todas las pulsaciones.
  5. Si pulsamos en un objeto movil, mostra su caja limite y lo guarda en m_pCurrentObject al igual que aplica un material diferente a el

void GameState::onLeftPressed(const OIS::MouseEvent &evt)
{
     if(m_pCurrentObject)
     {
          m_pCurrentObject->showBoundingBox(false);
          m_pCurrentEntity->getSubEntity(1)->setMaterial(m_pOgreHeadMat);
     }
 
     Ogre::Ray mouseRay = m_pCamera->getCameraToViewportRay(OgreFramework::getSingletonPtr()->m_pMouse->getMouseState().X.abs / float(evt.state.width),
     OgreFramework::getSingletonPtr()->m_pMouse->getMouseState().Y.abs / float(evt.state.height));
 
     m_pRSQ->setRay(mouseRay);
     m_pRSQ->setSortByDistance(true);
 
     Ogre::RaySceneQueryResult &result = m_pRSQ->execute();
     Ogre::RaySceneQueryResult::iterator itr;
 
     for(itr = result.begin(); itr != result.end(); itr++)
     {
         if(itr->movable)
         {
             OgreFramework::getSingletonPtr()->m_pLog->logMessage("MovableName: " + itr->movable->getName());
             m_pCurrentObject = m_pSceneMgr->getEntity(itr->movable->getName())->getParentSceneNode();
             OgreFramework::getSingletonPtr()->m_pLog->logMessage("ObjName " + m_pCurrentObject->getName());
             m_pCurrentObject->showBoundingBox(true);
             m_pCurrentEntity = m_pSceneMgr->getEntity(itr->movable->getName());
             m_pCurrentEntity->getSubEntity(1)->setMaterial(m_pOgreHeadMatHigh);
             break;
         }
     }
}
 


  • moveCamera(): Traslada la camara por el vector obtenido en getInput() (10 veces mas rapido si Shift se ha presionado)

void GameState::moveCamera()
{
    if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_LSHIFT))
        m_pCamera->moveRelative(m_TranslateVector);
    m_pCamera->moveRelative(m_TranslateVector / 10);
}

  • getInput(): Si no estamos en modo chat, queremos mover la camera por medio de las teclas, asi en este caso realiza un vector de traslacion que aplicado a la camera en moveCamera()

void GameState::getInput()
{
    if(m_bSettingsMode == false)
    {
        if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_A))
            m_TranslateVector.x = -m_MoveScale;
 
        if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_D))
            m_TranslateVector.x = m_MoveScale;
 
        if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_W))
            m_TranslateVector.z = -m_MoveScale;
 
        if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_S))
            m_TranslateVector.z = m_MoveScale;
     }
}

  • update(): Actualiza el estado completo:

  1. actualiza la GUI
  2. deja y elimina este estado desde la pila del estado activo si se pide
  3. actualiza la informacion en el panel de detalles
  4. determina la escala de movimiento dependiendo del tiempo pasado desde el ultimo frame
  5. resetea el vector traslacion de la camera
  6. consigue la entrada sin bufer y mueva la camera

void GameState::update(double timeSinceLastFrame)
{
     m_FrameEvent.timeSinceLastFrame = timeSinceLastFrame;
     OgreFramework::getSingletonPtr()->m_pTrayMgr->frameRenderingQueued(m_FrameEvent);
 
     if(m_bQuit == true)
     {
         popAppState();
         return;
     }
 
     if(!OgreFramework::getSingletonPtr()->m_pTrayMgr->isDialogVisible())
     {
         if(m_pDetailsPanel->isVisible())
         {
               m_pDetailsPanel->setParamValue(0, Ogre::StringConverter::toString(m_pCamera->getDerivedPosition().x));
               m_pDetailsPanel->setParamValue(1, Ogre::StringConverter::toString(m_pCamera->getDerivedPosition().y));
               m_pDetailsPanel->setParamValue(2, Ogre::StringConverter::toString(m_pCamera->getDerivedPosition().z));
               m_pDetailsPanel->setParamValue(3, Ogre::StringConverter::toString(m_pCamera->getDerivedOrientation().w));
               m_pDetailsPanel->setParamValue(4, Ogre::StringConverter::toString(m_pCamera->getDerivedOrientation().x));
               m_pDetailsPanel->setParamValue(5, Ogre::StringConverter::toString(m_pCamera->getDerivedOrientation().y));
               m_pDetailsPanel->setParamValue(6, Ogre::StringConverter::toString(m_pCamera->getDerivedOrientation().z));
               if(m_bSettingsMode)
                   m_pDetailsPanel->setParamValue(7, "Buffered Input");
               else
                   m_pDetailsPanel->setParamValue(7, "Un-Buffered Input");
         }
      }
 
      m_MoveScale = m_MoveSpeed * timeSinceLastFrame;
      m_RotScale = m_RotateSpeed * timeSinceLastFrame;
 
      m_TranslateVector = Vector3::ZERO;
 
      getInput();
      moveCamera();
}


  • buildGUI(): Inserta todos los elementos necesarios de GuI en el SDKTrayManager

void GameState::buildGUI()
{
    OgreFramework::getSingletonPtr()->m_pTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createLabel(OgreBites::TL_TOP, "GameLbl", "Game mode", 250);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->showCursor();
 
    Ogre::StringVector items;
    items.push_back("cam.pX");
    items.push_back("cam.pY");
    items.push_back("cam.pZ");
    items.push_back("cam.oW");
    items.push_back("cam.oX");
    items.push_back("cam.oY");
    items.push_back("cam.oZ");
    items.push_back("Mode");
 
    m_pDetailsPanel = OgreFramework::getSingletonPtr()->m_pTrayMgr->createParamsPanel(OgreBites::TL_TOPLEFT, "DetailsPanel", 200, items);
    m_pDetailsPanel->show();
 
    Ogre::String infoText = "[TAB] - Switch input mode\n\n[W] - Forward / Mode up\n[S] - Backwards/ Mode down\n[A] - Left\n";
    infoText.append("[D] - Right\n\nPress [SHIFT] to move faster\n\n[O] - Toggle FPS / logo\n");
    infoText.append("[Print] - Take screenshot\n\n[ESC] - Exit");
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createTextBox(OgreBites::TL_RIGHT, "InfoPanel", infoText, 300, 220);
 
    Ogre::StringVector chatModes;
    chatModes.push_back("Solid mode");
    chatModes.push_back("Wireframe mode");
    chatModes.push_back("Point mode");
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createLongSelectMenu(OgreBites::TL_TOPRIGHT, "ChatModeSelMenu", "ChatMode", 200, 3, chatModes);
}


  • itemSelected(): funcion de devolucion de llamada lanzada cuando una seleccion de menu cambia. Como resultado cuando cambia el modo de poligo de la camera.

void GameState::itemSelected(OgreBites::SelectMenu* menu)
{
     switch(menu->getSelectionIndex())
     {
      case 0:
          m_pCamera->setPolygonMode(Ogre::PM_SOLID);break;
      case 1:
          m_pCamera->setPolygonMode(Ogre::PM_WIREFRAME);break;
      case 2:
          m_pCamera->setPolygonMode(Ogre::PM_POINTS);break;
     }
}

PauseState.hpp


La clase PauseState es uno de las implementaciones actuales de AppState. Hereda de AppState se asegura que todos los estados tienen las mismas funciones comunes como enter(), exit(), pause(), resume() o update().

  • DECLARE_APPSTATE_CLASS(PauseState): Esta linea llama a la macro definida en AppState.hpp, haciendo esta clase un estado valido de aplicacion

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#ifndef PAUSE_STATE_HPP
#define PAUSE_STATE_HPP
 
//|||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AppState.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class PauseState : public AppState
{
public:
      PauseState();
 
      DECLARE_APPSTATE_CLASS(PauseState)
 
      void enter();
      void createScene();
      void exit();
 
      bool keyPressed(const OIS::KeyEvent &keyEventRef);
      bool keyReleased(const OIS::KeyEvent &keyEventRef);
 
      bool mouseMoved(const OIS::MouseEvent &evt);
      bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
      bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
 
      void buttonHit(OgreBites::Button* button);
      void yesNoDialogClosed(const Ogre::DisplayString& question, bool yesHit);
 
      void update(double timeSinceLastFrame);
 
private:
      bool m_bQuit;
      bool m_bQuestionActive;
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
// |||||||||||||||||||||||||||||||||||||||||||||||



PauseState.cpp


  • constructor(): Inicializa valores
  • enter(): Establece los valores basicos e comienza la GUI

// |||||||||||||||||||||||||||||||||||||||||||
 
#include "PauseState.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
using namespace Ogre;
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
PauseState::PauseState()
{
    m_bQuit = false;
    m_bQuestionActive = false;
    m_FrameEvent = Ogre::FrameEvent();
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void PauseState::enter()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering PauseState...");
 
    m_pSceneMgr = OgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "PauseSceneMgr");
    m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.7f, 0.7f, 0.7f));
 
    m_pCamera = m_pSceneMgr->createCamera("PauseCam");
    m_pCamera->setPosition(Vector3(0, 25, -50));
    m_pCamera->lookAt(Vector3(0, 0, 0));
    m_pCamera->setNearClipDistance(1);
    m_pCamera->setAspectRatio(Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualWidth()) /
    Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualHeight()));
 
    OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->destroyAllWidgets();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->showCursor();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "BackToGameBtn", "Return to GameState", 250);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "BackToMenuBtn", "Return to Menu", 250);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "ExitBtn", "Exit AdvancedOgreFramework", 250);
    OgreFramework::getSingletonPtr()->m_pTrayMgr->createLabel(OgreBites::TL_TOP, "PauseLbl", "Pause mode", 250);
 
    m_bQuit = false;
 
    createScene();
}

  • createScene(): LLenaria la escena aqui, pero solo tenemos elementos de GUI
  • exit(): Autoexplicativo no?

void PauseState::createScene()
{
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void PauseState::exit()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Leaving PauseState...");
 
    m_pSceneMgr->destroyCamera(m_pCamera);
    if(m_pSceneMgr)
        OgreFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr);
 
    OgreFramework::getSingletonPtr()->m_pTrayMgr->clearAllTrays();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->destroyAllWidgets();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->setListener(0);
}

  • keyPressed(): Comprueba si tenemos que dejar y pasar todos los otros eventos en el OgreFramework
  • keyReleased(): Pasa todos los otros eventos al OgreFramework
  • mouseMoved(): Pasa todos los eventos al SDKTrayManager
  • mousePressed(): Pasa eventos al SDKTrayManager
  • mouseReleased(): Pasa eventos al SDKTrayManager

bool PauseState::keyPressed(const OIS::KeyEvent &keyEventRef)
{
    if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_ESCAPE))
    {
        m_bQuit = true;
        return true;
    }
 
    OgreFramework::getSingletonPtr()->keyPressed(keyEventRef);
 
    return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool PauseState::keyReleased(const OIS::KeyEvent &keyEventRef)
{
     OgreFramework::getSingletonPtr()->keyReleased(keyEventRef);
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool PauseState::mouseMoved(const OIS::MouseEvent &evt)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseMove(evt)) return true;
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool PauseState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseDown(evt, id)) return true;
     return true;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
bool PauseState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
     if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseUp(evt, id)) return true;
     return true;
}


  • update(): Actualiza el estado, asi primero la GUI, entonces comprueba para dejar la condicion
  • buttonHit(): Se lanza cuando un boton es pulsado. Entonces reacciona cambiando los estados de aplicacion o mostrando el dialogo de GUI
  • yesNoDialogClosed(): Muestra un dialogo desplegable para confirmar que el usuario quiere dejar la aplicacion.

void PauseState::update(double timeSinceLastFrame)
{
    m_FrameEvent.timeSinceLastFrame = timeSinceLastFrame;
    OgreFramework::getSingletonPtr()->m_pTrayMgr->frameRenderingQueued(m_FrameEvent);
 
    if(m_bQuit == true)
    {
        popAppState();
        return;
    }
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void PauseState::buttonHit(OgreBites::Button *button)
{
    if(button->getName() == "ExitBtn")
    {
     OgreFramework::getSingletonPtr()->m_pTrayMgr->showYesNoDialog("Sure?", "Really leave?");
     m_bQuestionActive = true;
    }
    else if(button->getName() == "BackToGameBtn")
        m_bQuit = true;
    else if(button->getName() == "BackToMenuBtn")
        popAllAndPushAppState(findByName("MenuState"));
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void PauseState::yesNoDialogClosed(const Ogre::DisplayString& question, bool yesHit)
{
     if(yesHit == true)
         shutdown();
     else
         OgreFramework::getSingletonPtr()->m_pTrayMgr->closeDialog();
 
     m_bQuestionActive = false;
}
 


DemoApp.hpp


La clase DemoApp es la aplicacion central la organizar la posicion. Solo tiene dos variables miembro:

  • El manejador del estado de aplicacion
  • un indicador de si la aplicacion esta apagada o no.

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#ifndef OGRE_DEMO_HPP
#define OGRE_DEMO_HPP
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "AdvancedOgreFramework.hpp"
#include "AppStateManager.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
class DemoApp
{
public:
        DemoApp();
        ~DemoApp();
 
        void startDemo();
 
private:
        AppStateManager* m_pAppStateManager;
};
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
#endif
 
// |||||||||||||||||||||||||||||||||||||||||||||||

DemoApp.cpp


Archivo central de la aplicacion, maneja todo:
  • DemoApp(): Constructor
  • ~DemoApp(): Destructor
  • startDemo(): Comienza la aplicacion:

  1. Lanza el OgreFramework
  2. inicializa Ogre (mediante el OgreFramework)
  3. crea una instancia de AppStateManager
  4. crea los estados de aplicacion (MenuState, GameState y PauseState)
  5. ordena al AppStateManager que comience su funcion de bucle principal

// |||||||||||||||||||||||||||||||||||||||||||||||
 
#include "DemoApp.hpp"
 
#include "MenuState.hpp"
#include "GameState.hpp"
#include "PauseState.hpp"
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
DemoApp::DemoApp()
{
     m_pAppStateManager = 0;
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
DemoApp::~DemoApp()
{
     delete m_pAppStateManager;
     delete OgreFramework::getSingletonPtr();
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||
 
void DemoApp::startDemo()
{
     new OgreFramework();
     if(!OgreFramework::getSingletonPtr()->initOgre("AdvancedOgreFramework", 0, 0))
          return;
 
     OgreFramework::getSingletonPtr()->m_pLog->logMessage("Demo initialized!");
 
     m_pAppStateManager = new AppStateManager();
 
     MenuState::create(m_pAppStateManager, "MenuState");
     GameState::create(m_pAppStateManager, "GameState");
     PauseState::create(m_pAppStateManager, "PauseState");
 
     m_pAppStateManager->start(m_pAppStateManager->findByName("MenuState"));
}
 
// |||||||||||||||||||||||||||||||||||||||||||||||

Notas


1. Si quieres, puedes declarar todos los miembros de Ogre en la clase OgreFramework (Root,Camera, RenderWindow, SceneManager, ...) privados e implementar los metodos con ellos. Estos es a tu eleccion.

2. Si Ogre::WindowEventUtilities::messagePump() no te funciona, intenta esto:

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
{
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
       if (msg.message == WM_QUIT)
           m_bShutdown = true;
       else
       {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
       }
    }
}
#endif


3. En Mac, la funcion Sleep() esta escrita con una s minuscula al comienzo, tienes que cambiar esto (si quieres ambas en tu codigo) usa esto en su lugar:

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
Sleep(1000);
#elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE
sleep(1000);
#endif

4. En este comentario de foro encontraras una lista de todos los cambios necesarios para conseguir que funcione con XCode en Mac.

Conclusion


Fatigado pero feliz, el joven programador cierra el libro secreto, sintiendo su cabeza llena de todos los grandes conocimientos.