Tutorial Básico 8 usando Múltiples SceneManagers



external image dl1773
En este corto Tutorial trataremos como cambiar entre múltiples manejadores de escena.

Los problemas que encuentres mientras trabajes en este tutorial deberías consultarlos en los Foros de Ayuda.

Prerrequisitos


  • Este tutorial asume que tienes conocimientos sobre programacion en C++ y eres capaz de compilar e instalar una aplicación de Ogre.
  • Este tutorial también asume que has creado un proyecto usando el Tutorial Framework Ogre Wiki, manualmente, usando CMake o el AppWizard de Ogre - mirar Creando una aplicación para más instrucciones.
  • Este tutorial se compila sobre la versión anterior de los tutoriales básicos, así que se asume que has trabajado con ellos.

Cuando mires el tutorial deberías ir lentamente añadiendo código a tu proyecto y mirando los resultados. Puedes ver el código fuente para ver el estado final del tutorial aquí. Si tienes problemas con el código, deberías comparar tu fuente del proyecto al resultado final.


Comenzando


El código Inicial


Modifica la cabecera de tu clase Tutorial básico 8 para que sea como esto:
#ifndef BasicTutorial8_h_
#define BasicTutorial8_h_
 
#include "BaseApplication.h"
 
class BasicTutorial8 : public BaseApplication
{
public:
 BasicTutorial8(void);
 virtual ~BasicTutorial8(void);
 
protected:
 virtual void createScene(void);
 virtual void chooseSceneManager(void);
 virtual void createCamera(void);
 virtual void createViewports(void);
 virtual void createFrameListener(void);
 
 virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
 
 
 // OIS::KeyListener
 virtual bool keyPressed( const OIS::KeyEvent &arg );
 virtual bool keyReleased( const OIS::KeyEvent &arg );
 // OIS::MouseListener
 virtual bool mouseMoved( const OIS::MouseEvent &arg );
 virtual bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id );
 virtual bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id );
 
 private:
 Ogre::SceneManager* mPrimarySceneMgr;
 Ogre::SceneManager* mSecondarySceneMgr;
 bool mDual;
 
 virtual void setupViewport(Ogre::SceneManager *curr);
 virtual void dualViewport(Ogre::SceneManager *primarySceneMgr, Ogre::SceneManager *secondarySceneMgr);
};
 
#endif #ifndef BasicTutorial8_h_
Asegúrate de que el archivo de implementación Tutorial Básico 7 se parece a esto:
#include "BasicTutorial8.h"
#define CAMERA_NAME "SceneCamera"
 
// -------------------------------------------------------------------------------------
BasicTutorial8::BasicTutorial8(void)
 :mPrimarySceneMgr(0),
 mSecondarySceneMgr(0),
 mDual(false)
{
}
// -------------------------------------------------------------------------------------
BasicTutorial8::~BasicTutorial8(void)
{
}
// -------------------------------------------------------------------------------------
 
// Funciones Locales
void BasicTutorial8::setupViewport(Ogre::SceneManager *curr)
{
}
 
void BasicTutorial8::dualViewport(Ogre::SceneManager *primarySceneMgr, Ogre::SceneManager *secondarySceneMgr)
{
}
 
static void swap(Ogre::SceneManager *&first, Ogre::SceneManager *&second)
{
 Ogre::SceneManager *tmp = first;
 first = second;
 second = tmp;
}
 
// -------------------------------------------------------------------------------------
void BasicTutorial8::createScene(void)
{
}
 
void BasicTutorial8::chooseSceneManager(void)
{
}
 
void BasicTutorial8::createCamera()
{
}
 
void BasicTutorial8::createViewports()
{
}
 
void BasicTutorial8::createFrameListener(void)
{
 Ogre::LogManager::getSingletonPtr()->logMessage("* Initializing OIS *");
 OIS::ParamList pl;
 size_t windowHnd = 0;
 std::ostringstream windowHndStr;
 
 mWindow->getCustomAttribute("WINDOW", &windowHnd);
 windowHndStr << windowHnd;
 pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
 
 mInputManager = OIS::InputManager::createInputSystem( pl );
 
 mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
 mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));
 
 mMouse->setEventCallback(this);
 mKeyboard->setEventCallback(this);
 
 // Establece el tamanyo inicial de recorte del raton
 windowResized(mWindow);
 
 // registralo como un Window listener
 Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
 
 mRoot->addFrameListener(this);
}
 
bool BasicTutorial8::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
 if(mWindow->isClosed())
 return false;
 
 if(mShutDown)
 return false;
 
 // necesitas capturar/actualizar cada dispositivo
 mKeyboard->capture();
 mMouse->capture();
 
 return true;
}
 
bool BasicTutorial8::keyPressed( const OIS::KeyEvent &arg )
{
 if (arg.key == OIS::KC_ESCAPE)
 {
 mShutDown = true;
 }
 
 return true;
}
 
bool BasicTutorial8::keyReleased( const OIS::KeyEvent &arg )
{
 return true;
}
 
bool BasicTutorial8::mouseMoved( const OIS::MouseEvent &arg )
{
 return true;
}
 
bool BasicTutorial8::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
 return true;
}
 
bool BasicTutorial8::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
return true;
}
 
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif
 
#ifdef cplusplus
extern "C" {
#endif
 
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
 int main(int argc, char *argv[])
#endif
 {
 // Crea un objeto de aplicacion
 BasicTutorial8 app;
 
 try {
 app.go();
 } catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
 MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
 std::cerr << "An exception has occured: " <<
 e.getFullDescription().c_str() << std::endl;
#endif
 }
 
 return 0;
 }
 
#ifdef __cplusplus
}
#endif

Asegúrate de que puedes compilar este código antes de continuar, ejecutando el programa que sólo mostrará una ventana en blanco. Presiona Esc para salir.

Creando la Aplicación


Creando los SceneManagers


Hemos hablado previamente de como seleccionar tu SceneManager, así que no entraremos en detalles sobre esta función. La única cosa que cambia es que estamos creando dos de ellos. Busca la función chooseSceneManager y añade el siguiente código:
mPrimarySceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC, "primary");
mSecondarySceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC, "secondary");

Creando las Cámaras

La siguiente cosa que tenemos que hacer es crear un objeto Cámara para cada uno de los SceneManagers. La única diferencia desde los tutoriales anteriores es que estamos creando dos de ellos, con el mismo nombre. Busca la función createCamera y añade el siguiente código:
mPrimarySceneMgr->createCamera(CAMERA_NAME);
mSecondarySceneMgr->createCamera(CAMERA_NAME);

Creando los Viewports


En, creando el Viewport de esta aplicación, cogimos parte de los tutoriales anteriores. Cuando crear un Viewport, debes hacer dos cosas: establecer el Viewport en si mismo y entonces establecer la relación de aspecto de la cámara a usar. Para comenzar, añade el siguiente código a la función createViewports:
setupViewport(mPrimarySceneMgr);
El código actual para establecer el Viewport reside en nuestra función setupViewport() ya que usaremos este código otra vez. La primera cosa que necesitamos hacer es eliminar los anteriores Viewports creados. Ninguno ha sido creado todavía, pero cuando llamemos a la función otra vez más tarde, necesitaremos estar seguros de que son eliminados todos antes de crear los nuevos. Después de que hemos establecido los Viewports como los tenemos en los anteriores tutoriales. Añade el código siguiente a la función setupViewport() en lo alto del archivo:
mWindow->removeAllViewports();
 
Ogre::Camera *cam = curr->getCamera(CAMERA_NAME); // La Camara
Ogre::Viewport *vp = mWindow->addViewport(cam); // Nuestro Viewport enlazado a la camara
 
vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
cam->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));

Creando la Escena


Finalmente, necesitamos crear una escena para cada SceneManager para que la contenga. No será algo complejo, sólo diferente así podremos cambiarlos cuando queramos. Busca la función createScene y añade el siguiente código:
// Instala el espacio del SceneManager
mPrimarySceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");
// Instala el espacio de nubes de SceneManager
mSecondarySceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);
Asegúrate de que tu código compila antes de seguir. Ejecuta tu programa, debería mostrar una caja celeste. Nada nuevo.

Añadiendo Funcionalidad


SceneManagers Duales


La primera pieza de la funcionalidad que queremos añadir al programa es la que permite al usuario renderizar ambos SceneManagers uno al lado del otro. Cuando la tecla V es presionada cambiamos a modo dual. El plan básico para esto es simple. Apaga el modo de Viewport dual, simplemente llama a setupViewport (que hemos creado en la sección anterior) con el SceneManager primario para recrear el Viewport en modo único. Guardamos el estado del Viewport con la variable mDual. Añade el siguiente código al final de la función keyPressed():
else if(arg.key == OIS::KC_V){
 mDual = !mDual;
 
if (mDual)
 dualViewport(mPrimarySceneMgr, mSecondarySceneMgr);
else
 setupViewport(mPrimarySceneMgr);
}
Ahora tenemos que cambiar la variable mDual y llamar a la función apropiada basada en el modo en que estamos. Ahora definimos la función dualViewport() que contiene el código para mostrar dos Viewports a la vez.

Para poder mostrar dos SceneManagers uno al lado del otro, tenemos que hacer básicamente lo mismo que hicimos con la función setupViewport(). La única diferencia es que crearemos dos Viewports, uno para cada Cámara en nuestros SceneManagers. Añadir el siguiente código a la función dualViewport():
mWindow->removeAllViewports();
 
Ogre::Viewport *vp = 0;
Ogre::Camera *cam = primarySceneMgr->getCamera(CAMERA_NAME);
vp = mWindow->addViewport(cam, 0, 0, 0, 0.5, 1);
vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
cam->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
 
cam = secondarySceneMgr->getCamera(CAMERA_NAME);
vp = mWindow->addViewport(cam, 1, 0.5, 0, 0.5, 1);
vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
cam->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
Todo esto debería ser familiar excepto por los parámetros extra que hemos añadido a la llamada de la función addViewport. El primer parámetro a esta función es todavía la cámara que vamos a usar. El segundo parámetro es el orden z del Viewport. Un valor alto de z se posiciona encima de los valores bajos de z. Nota que no puedes tener dos Viewports con el mismo orden z, incluso si no se superponen. Los siguientes dos parámetros son las posiciones izquierda y arriba (top y left respectivamente) del Viewport, que deben estar entre 0 y 1. Finalmente, los últimos dos parámetros son el width (ancho) y height (alto) del Viewport como un porcentaje de la pantalla (otra vez, deben estar entre 0 y 1). Así en este caso, el primer Viewport que crearemos estará en la posición (0,0) y cogerá la mitad de la pantalla horizontalmente y toda la pantalla verticalmente. El segundo Viewport estará en la posición (0.5, 0) y también tomará la mitad del espacio horizontal y todo el espacio vertical.

Compila y ejecuta la aplicación. Presiona V para que puedas mostrar los dos SceneManagers al mismo tiempo.

Cambiando entre SceneManagers


La última pieza de funcionalidad que queremos añadir a nuestro programa es el intercambio de SceneManagers y la tecla C se presiona. Para hacer esto, primero intercambiaremos las variables primarySceneMgr y secondarySceneMgr así que cuando las funciones setupViewport() o dualViewport() sean llamadas, no necesitaremos preocuparnos sobre que SceneManager esta en que variable. El SceneManager primario será siempre mostrado en modo único, y el primario siempre estará en el lado izquierdo en el modo de dualViewport. Añadir el siguiente código al final de la función keyPressed():
else if(arg.key == OIS::KC_C){
 swap(mPrimarySceneMgr, mSecondarySceneMgr);
 
 if (mDual)
 dualViewport(mPrimarySceneMgr, mSecondarySceneMgr);
 else
 setupViewport(mPrimarySceneMgr);
}
Después de intercambiar las variables SceneManagers, realizaremos el cambio. Todo lo que tenemos que hacer es llamar a la función de instalación apropiada del Viewport dependiendo de si estamos en modo único o dual.

Eso es todo! Compila y ejecuta la aplicación. Podemos ahora cambiar los SceneManagers con la tecla C, y cambiar de modo único a dual con la tecla V.

Conclusión


Superposiciones


Estoy seguro de que has notado en tu programa que cuando estas en modo dual, Ogre debug lo superpone mostrando ambos lado. Apagar el renderizado de superposición es básico. Usa Viewport::setOverlaysEnabled para apagar o encender. Es relativamente fácil cambiar el código completo de este tutorial, así que si estas confuso de como se hace , mira la página de detalles.

Una última nota


Siempre ten en mente que la clase Viewport, aunque no tenga mucha funcionalidad en si misma, es la llave para todos los renderizados en Ogre. Da igual cuantos SceneManagers crees o cuantas Cámaras en cada SceneManager, no se renderizarán en la ventana a menos que establezcas un Viewport para cada Cámara que estes mostrando. También no olvides limpiar los Viewports que no estas usando, antes de crear más.