Tutorial Intermedio 4 Selección de Volumen y ManualObject Básico


Introducción


En este tutorial trataremos sobre como usar la selección de volumen. La idea es que cuando pulses y arrastres el ratón sobre la pantalla, un rectángulo blanco trazará el área que has seleccionado. Cuando muevas el ratón, todos los objetos dentro del área de selección seran resaltados. Para cumplir con esto tenemos que aprender a como usar dos objetos: ManualObject (para crear el rectángulo) y PlaneBoundVolumeListSceneQuery. Nota que mientras tanto trataremos algunos usos básicos de ManualObject, esto es sólo una introducción, y no un tutorial sobre como crear completamente objetos 3D con él. Sólo cubriremos lo que necesitamos usar.

Puedes encontrar el código de este tutorial aquí. Deberías ir añadiendo código poco a poco segun avance el tutorial y mirar los resultados.

Prerrequisitos


Asegúrate de que tienes el Tutorial Framework Básico. Entonces asegúrate de que tu cabecera de aplicación y el archivo cpp se parecen a este:

IntermediateTutorial4.h
#ifndef IntermediateTutorial4_h_
#define IntermediateTutorial4_h_
 
#include "BaseApplication.h"
 
#include <CEGUI.h>
#include <CEGUISchemeManager.h>
#include <RendererModules/Ogre/CEGUIOgreRenderer.h>
 
class IntermediateTutorial4 : public BaseApplication
{
public:
 IntermediateTutorial4(void);
 virtual ~IntermediateTutorial4(void);
 
protected:
 virtual void createScene(void);
 
 virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
 
 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);
 
 void performSelection(const Ogre::Vector2& first, const Ogre::Vector2& second);
 void deselectObjects();
 void selectObject(Ogre::MovableObject* obj);
 
private:
 Ogre::Vector2 mStart, mStop;
 Ogre::PlaneBoundedVolumeListSceneQuery* mVolQuery;
 std::list<Ogre::MovableObject*> mSelected;
 bool mSelecting;
 CEGUI::OgreRenderer* mGUIRenderer;
 
 static void swap(float& x, float& y);
};
#endif // #ifndef IntermediateTutorial4_h_
IntermediateTutorial.cpp
#include "IntermediateTutorial4.h"
 
// -------------------------------------------------------------------------------------
IntermediateTutorial4::IntermediateTutorial4(void)
{
}
// -------------------------------------------------------------------------------------
IntermediateTutorial4::~IntermediateTutorial4(void)
{
}
// -------------------------------------------------------------------------------------
void IntermediateTutorial4::createScene(void)
{
 mSceneMgr->setAmbientLight(Ogre::ColourValue(1.0f, 1.0f, 1.0f));
 
 for(int i = 0; i < 10; ++i)
 {
 for(int j = 0; j < 10; ++j)
 {
 Ogre::Entity* ent = mSceneMgr->createEntity("Robot" + Ogre::StringConverter::toString(i+j*10), "robot.mesh");
 Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(1 * 15, 0, j * 15));
 node->attachObject(ent);
 node->setScale(0.1f, 0.1f, 0.1f);
 }
 }
 
 mCamera->setPosition(-60, 100, -60);
 mCamera->lookAt(60, 0, 60);
 
 mGUIRenderer = &CEGUI::OgreRenderer::bootstrapSystem();
 
 CEGUI::SchemeManager::getSingleton().create((CEGUI::utf8*)"TaharezLook.scheme");
 CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");
}
 
bool IntermediateTutorial4::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
 return BaseApplication::frameRenderingQueued(evt);
}
 
bool IntermediateTutorial4::mouseMoved(const OIS::MouseEvent& arg)
{
 CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
 return true;
}
 
bool IntermediateTutorial4::mousePressed(const OIS::MouseEvent& arg, OIS::MouseButtonID id)
{
 return true;
}
 
bool IntermediateTutorial4::mouseReleased(const OIS::MouseEvent& arg, OIS::MouseButtonID id)
{
 return true;
}
 
void IntermediateTutorial4::performSelection(const Ogre::Vector2& first, const Ogre::Vector2& second)
{
}
 
void IntermediateTutorial4::deselectObjects()
{
}
 
void IntermediateTutorial4::selectObject(Ogre::MovableObject* obj)
{
}
 
void swap(float& x, float& y)
{
 float temp = x;
 x = y;
 y = temp;
}
 
#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
 IntermediateTutorial4 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 este código se compila antes de continuar. Cuando lo ejecutes, deberías ser capaz de mover el cursor del ratón, pero la aplicación no hará otra cosa por ahora. Presiona escape para salir.

ManualObjects

Un curso de choque sobre Objetos 3D


Antes de que nos sumerjamos directamente en el proceso de hacer mallas, problemente sea útil hablar sobre como es una malla, de que esta hecha. Piensa que es una simplificación, una malla consiste de dos partes; el búfer de vértices y el búfer de índices.

Los búferes de vértice definen puntos en el espacio 3D. Cada elemento en el búfer de vértice esta definido por varios atributos que puedes establecer. El único atributo que debes ajustar es la posición de los vértices. por otro lado, hay muchas propiedades opcionales que puedes ajustar, como son el color del vértice, las coordenadas de textura, y demás. Cual necesites usar es independiente que lo que intentas hacer con la malla.

Los búferes de índice "conectan los puntos" seleccionando puntos del búfer de vertices. Cada tres índices especificados en el búfer de índice es un único triángulo dibujado por la GPU. El orden en el que seleccionas los vértices en el búfer de índices le dice a la gráfica el modo de las caras del triángulo. Un triángulo que esta dibujado al contrario de las agujas del reloj esta mirando hacia ti, por el otro lado a favor de la agujas esta apuntando donde miras. Normalmente sólo el frente de un triángulo se renderiza, así que es importante estar seguro de que tus triángulos estas instalados apropiadamente.

Piensa que todas las mallas tienen un búfer de vértices, pero no todas tendran un búfer de índices. Por ejemplo, la malla que estamos creando no tendrá un búfer de índices ya que queremos crear un rectángulo vacío (como oposición a un rectángulo lleno). Finalmente, nota que los búferes de vértices e índice son normalmente guardados en la tarjeta de video, en su memoria, así que tu software puede sólo enviar un simple, y discreto conjunto de comandos para decirle que use estos búferes predefinidos para renderizar la malla entera 3D.

Introducción


Hay dos formas de crear tu propia malla dentro de Ogre. El primer modo es crear una subclase del objeto SimpleRenderable y proporcionarle los búferes de vértice e índice directamente. Este es el modo más directo de crear una, pero es también el más críptico. El code snippet Generando una Malla muestra un ejemplo de esto. Para hacer las cosas más fáciles, Ogre proporciona una interfaz más bonita llamada ManualObject, que te permite usar algunas funciones simples para definir una malla en vez de escribir datos crudos a los objetos del búfer. En vez de coger la posición, color, y demás en el búfer, puedes llamar simplemente a las funciones "position" y "colour".

En este tutorial necesitamos crear un rectángulo blanco para mostrar cuando estamos cogiendo los objetos seleccionados del ratón. No hay en Ogre una clase que nos permita mostrar rectángulos 2D. Tendremos que usar un modo de hacerlo por nuestra cuenta. Podríamos usar un Overlay y redimensionarlo para que muestre el rectángulo de selección, pero el problema sobre como hacer esto es el modo en que la imagen que hemos usado para el rectángulo de selección podría ampliarse y mostrarse mal. En vez de eso, generaremos una malla muy simple 2D para actuar como nuestro rectángulo de selección.

Caja de Selección


Ahora, podemos manejar todo lo de la caja de selección dentro de nuestro tutorial de aplicación. Es muy buena practica guardar objetos separados en clases separadas, porque hace las cosas más fáciles. Asi que básicamente para la caja de selección tenemos que hacer un archivo separado para la cabecera y para cpp llamado SelectionBox.h y SelectionBox.cpp respectivamente, y vamos a declarar esta clase como cualquier otra clase excepto que vamos a sacarla de Ogre::ManualObject. Asi que tienes que hacer que tu cabecera y cpp se parezcan a esto.

SelectionBox.h
#ifndef SelectionBox_h_
#define __SelectionBox_h_
 
#include "OgreManualObject.h"
 
class SelectionBox : public Ogre::ManualObject
{
public :
 SelectionBox(const Ogre::String& name);
 ~SelectionBox(void);
 
 void setCorners(float left, float top, float right, float bottom);
 void setCorners(const Ogre::Vector2& topLeft, const Ogre::Vector2& topRight);
};
 
#endif
SelectionBox.cpp
#include "SelectionBox.h"
 
SelectionBox::SelectionBox(const Ogre::String& name): Ogre::ManualObject(name)
{
}
 
SelectionBox::~SelectionBox()
{
}
 
void SelectionBox::setCorners(float left, float top, float right, float bottom)
{
}
 
void SelectionBox::setCorners(const Ogre::Vector2& topLeft, const Ogre::Vector2& bottomRight)
{
 setCorners(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
}

El Código


Cuando creamos el rectángulo de selección, tenemos que crearlo tal que será renderizado en 2D. También tendremos que asegurarnos que se renderizará cuando las superposiciones del renderizador de Ogre digan que esta en lo alto de todos los otros objetos de la pantalla. Hacer esto es muy fácil. Encuentra el constructor de SelectionBox y añade el siguiente código:
setRenderQueueGroup(RENDER_QUEUE_OVERLAY); // Cuando uses esto asegurate de que Depth Check esta a Off en el material.
setUseIdentityProjection(true);
setUseIdentityView(true);
setQueryFlags(0);
La primera función establece la cola de renderizado para el objeto para que sea la cola de Superposiciones. Las siguientes dos funciones establecen las matrices de proyección y vista para que sean la matriz identidad. Las matrices de Vista y Proyección son usadas por muchos sistemas de renderizado (tales como OpenGL y DirectX) para definir donde van los objetos en el mundo. Ya que Ogre abstrae esto para nosotros, no nos meteremos con estas matrices. En vez de eso necesitamos saber si has establecido la matriz de proyección y es de identidad, como tenemos aqui, hemos creado basicamente un objeto 2D. Cuando definimos este objeto, el sistema de coordenadas cambia un poco. No trataremos más con el eje Z (si quieres el eje Z, establece el value a -1). En vez de eso tenemos un nuevo sistema de coordenadas con X e Y entre -1 y 1 inclusive. Finalmente, estableceremos las banderas de consulta del objeto para que sea 0, lo que preservará el rectangulo de selección de ser incluido en los resultados de la consulta.

Ahora que el objeto esta establecido, necesitamos compilar el rectángulo. tenemos un pequeño inconveniente antes de comenzar. Llamaremos a esta función con las posiciones del ratón. Las que le demos, un número entre 0 y 1 para las coordenadas -1,1. El cursor del ratón en CEGUI define el alto de la pantalla a 0, el fondo a 1. En nuestro nuevo sistema de coordenadas, el alto de la pantalla es +1, el fondo es -1. Afortunadamente, unas rápidas conversiones nos ayudaran con este problema. Busca la función setCorners y añade el siguiente código:
left = left * 2 - 1;
right = right * 2 - 1;
top = 1 - top * 2;
bottom = 1 - bottom * 2;
Ahora las posiciones estan en el nuevo sistema de coordenadas. Lo siguiente que necesitamos es construir el objeto. Para hacer esto, tenemos que llamar al método begin. Que toma dos parámetros, el nombre del material a usar para esta sección del objeto, y la operación de renderizado a usar para dibujarlo. Ya que no estamos poniendo una textura , dejaremos el material en blanco. El segundo parámetro es la RenderOperation. Podemos renderizar la malla usando puntos, líneas, o triángulos. Podríamos usar triángulos si estuviéramos renderizando una malla completa, pero ya que tenemos un rectángulo vacío, usaremos el strip de línea. El strip de línea dibuja una línea para cada vértice desde el vértice previo que has definido. Asi que podemos crear nuestro rectángulo, definimos 5 puntos (el primero y el último punto son el mismo para conectar el rectángulo entero):
clear();
begin("", RenderOperation::OT_LINE_STRIP);
 position(left, top, -1);
 position(right, top, -1);
 position(right, bottom, -1);
 position(left, bottom, -1);
 position(left, top, -1);
end();

Nota que ya que estamos llamando a este muchas veces, tenemos que añadir la llamada al limpiador en el comienzo para eliminar el rectángulo previo antes de redibujarlo. Cuando definimos un ManualObject, llamas a begin/end múltiples veces para crear múltiples submallas (lo que puede tener diferentes materiales / RenderOperations). Nota que tenemos también el parámetro Z para que sea -1, ya que estamos intentando definir un objeto 2D lo que no usará este eje. Ajustándolo a -1 nos aseguraremos que no estamos en lo alto o nos hemos pasado, con la cámara cuando estamos renderizando.

La última cosa que necesitamos hacer es establecer la caja de límite para este objeto. muchos SceneManagers recortan objetos que estan fuera de la pantalla. A pesar de que estamos creando un objeto 2D, Ogre todavía sigue siendo un motor 3D, y trata nuestro objeto 2D como si estuviera en un espacio 3D. Esto significa que si creamos este objeto y lo acoplamos al SceneNode (como haremos en la siguiente sección), desaparecerá de nuestra vista cuando miremos a otro lado. Para arreglar esto necesitamos establecer la caja de límite del objeto para que sea infinita, así que la cámara siempre estará dentro de ella:
setBoundingBox(AxisAlignedBox::BOX_INFINITE);
Asegúrate de darte cuenta de que hemos añadido este código después de la llamada a clear. Cada vez que llamas a ManualObject::clear, la caja límite se resetea, asi que se cuidadoso si quieres crear otro ManualObject que se limpie a menudo porque la caja límite tendrá que establecerse cada vez que lo recreas.

Esto es todo lo que tenemos que hacer para la clase SelectionBox. Asegúrate de que tu código compila antes de continuar, pero date cuenta de que la funcionalidad no ha cambiado en el programa (todavía).

Selección de Volumen


Instalación


Antes de que podamos saltar a la selección de código, lo primero que tenemos que hacer es instalar algunas cosas. Lo primero de todo, incluir SelectionBox.h a nuestro IntermediateTutorial4.h, asi que podamos usarlo, y también necesitamos declarar una variable puntero para una caja de selección en la sección protegida de nuestra clase IntermediateTutorial4.
#include SelectionBox.h

SelectionBox* mSelectionBox;
Ahora que esta todo instalado necesitamos crear una instancia de la clase SelectionBox, y tener el SceneManager creado a una consulta de volumen. Anyade el siguiente codigo al final del createScene, este es el modo en que sabemos que el SceneManager ha sido inicializado:
mSelectionBox = new SelectionBox("SelectionBox");
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(mSelectionBox);
 
mVolQuery = mSceneMgr->createPlaneBoundedVolumeQuery(PlaneBoundedVolumeList());
Ahora, necesitamos asegurarnos de que el frame listener se limpia asi mismo cuando lo hemos hecho. Añade el siguiente código a ~IntermediateTutorial4:
mSceneMgr->destroyQuery(mVolQuery);
if(mSelectionBox)
{
delete mSelectionBox;
}
Nota que necesitamos limpiar la consulta SceneManager en vez de borrarla directamente.

Manejadores de Ratón


La característica que estamos intentando implementar es la selección de volumen. Esto significa que cuando el usuario pulsa con el ratón y arrastra por la pantalla un rectángulo se dibujará. Cuando suelten el ratón, todos los objetos dentro del rectángulo seran seleccionados. La primera cosa que necesitaremos hacer es manejar cuando el ratón se presiona. Necesitaremos guardar la posición de comienzo y establecer la SelectionBox para que sea visible. Busca la función mousePressed y añade el siguiente código:
if (id == OIS::MB_Left)
{
 CEGUI::MouseCursor *mouse = CEGUI::MouseCursor::getSingletonPtr();
 mStart.x = mouse->getPosition().d_x / (float)arg.state.width;
 mStart.y = mouse->getPosition().d_y / (float)arg.state.height;
 mStop = mStart;
 
 mSelecting = true;
 mSelectionBox->clear();
 mSelectionBox->setVisible(true);
}
Nota que estamos usando las coodenadas x e y CEGUI::MouseCursor y no las coordenadas del ratón de OIS. Esto es asi porque OIS algunas veces piensa que el ratón estan en un lugar diferente que lo que CEGUI esta mostrando. Para asegurarnos de estamos sincronizados con lo que el usuario esta viendo, usaremos las coordenadas de ratón de CEGUI.

La siguiente cosa que necesitamos hacer es parar de mostrar el rectángulo de selección y realizar la consulta de selección cuando el usuario suelte el ratón. Añadir el siguiente código al código de mouseReleased:
if (id == OIS::MB_Left)
{
 performSelection(mStart, mStop);
 mSelecting = false;
 mSelectionBox->setVisible(false);
}

Finalmente, cada vez que el ratón se mueva, necesitamos actualizar el rectangulo a las nuevas coordenadas:
if (mSelecting)
 {
 CEGUI::MouseCursor *mouse = CEGUI::MouseCursor::getSingletonPtr();
 mStop.x = mouse->getPosition().d_x / (float)arg.state.width;
 mStop.y = mouse->getPosition().d_y / (float)arg.state.height;
 
 mSelectionBox->setCorners(mStart, mStop);
 }
Ajustamos el vector mStop cada vez que el ratón se mueve asi que podemos usarlo para la función miembro setCorners. Compila y ejecuta tu aplicación. Puedes ahora dibujar un rectángulo usando el ratón.

PlaneBoundedVolumeListSceneQuery


Ahora que estamos renderizando apropiadamente la SelectionBox, necesitamos realizar la selección de volumen. Busca la función performSelection y añade el siguiente código:
float left = first.x, right = second.x,
top = first.y, bottom = second.y;
 
if (left > right)
 swap(left, right);
 
if (top > bottom)
 swap(top, bottom);
En esta sección de código, tenemos que asignar los parámetros del vector a la izquierda, derecha, arriba, y abajo. La claúsula if asegura que tenemos el valor más bajo en la izquierda y arriba.

Lo siguiente que tenemos que hacer es comprobar y mirar como de grande se hace el rectángulo. Si el rectángulo es demasiado pequeño, nuestro metodo de crear el plano limitador de volúmenes fallará y seleccionaremos demasiados o muy pocos objetos. Si el rectángulo es mejor que un cierto porcentaje de la pantalla, simplemente volveremos y no realizaremos la selección. He seleccionado 0.0001 como threshold para cancelar la consulta, con lo que deberías jugar para hacerlo en tu aplicación. También, en una aplicación real deberías probablemente encontrar el centro del rectángulo y realizar un RaySceneQuery estándar en vez de no hacer nada:
if ((right - left) * (bottom - top) < 0.0001)
 return;
Ahora vamos a la chicha de la función, y necesitamos realizar la consulta en si misma. PlaneBoundedVolumeQueries usa planos para encerrar un área, entonces selecciona cualquier objeto dentro de ese área. Para este ejemplo construiremos un área encerrada por cinco planos . Para crear estos planos fuera de nuestro rectángulo, crearemos 4 rayos, uno para cada esquina del rectángulo. Una vez tenemos estos 4 rayos, cogeremos los puntos a lo largo del rayo para crear los planos:
Ogre::Ray topLeft = mCamera->getCameraToViewportRay(left, top);
Ogre::Ray topRight = mCamera->getCameraToViewportRay(right, top);
Ogre::Ray bottomLeft = mCamera->getCameraToViewportRay(left, bottom);
Ogre::Ray bottomRight = mCamera->getCameraToViewportRay(right, bottom);
Ahora crearemos los planos. Nota que estamos cogiendo puntos en 100 unidades sobre el rayo. Esto se escogio arbitrariamente. Podriamos haber escogido 2 en vez de 100.
Ogre::PlaneBoundedVolume vol;
vol.planes.push_back(Ogre::Plane(topLeft.getPoint(3), topRight.getPoint(3), bottomRight.getPoint(3))); // Plano frontal
vol.planes.push_back(Ogre::Plane(topLeft.getOrigin(), topLeft.getPoint(100), topRight.getPoint(100))); // Plano superior
vol.planes.push_back(Ogre::Plane(topLeft.getOrigin(), bottomLeft.getPoint(100), topLeft.getPoint(100))); // Plano izquierdo
vol.planes.push_back(Ogre::Plane(bottomLeft.getOrigin(), bottomRight.getPoint(100), bottomLeft.getPoint(100))); // Plano inferior
vol.planes.push_back(Ogre::Plane(topRight.getOrigin(), topRight.getPoint(100), bottomRight.getPoint(100))); // Plano derecho
Estos planos tienen ahora definida una "caja abierta" que se extiende al infinito enfrente de la camara. Puedes pensar del rectangulo que dibujamos con el raton como el punto de terminacion de la caja solo en frente de la camara. Ahora que hemos creado los planos, necesitamos ejecutar la consulta:
Ogre::PlaneBoundedVolumeList volList;
volList.push_back(vol);
 
mVolQuery->setVolumes(volList);
Ogre::SceneQueryResult result = mVolQuery->execute();
Finalmente necesitamos manejar los resultados de la consulta. Primero necesitaremos deseleccionar todos los objetos previamente seleccionados, entonces seleccionaremos todos los objetos que estamos encontrando mediante la consulta. las funciones deselectObjects y selectObject ya estan preparadas para ser llenadas:
deselectObjects();
Ogre::SceneQueryResultMovableList::iterator iter;
for (iter = result.movables.begin(); iter != result.movables.end(); ++iter)
 selectObject(*iter);
Esto es todo lo que necesitamos hacer para la consulta. Nota que podemos tambien usar las banderas de consulta en las consultas de volumen. Mirar el tutorial anterior para mas informacion sobre banderas de consulta.

Ahora antes de que nos olvidemos, llenamos nuestros selectObject y DeselectObjects.
void IntermediateTutorial4::deselectObjects()
{
 std::list<Ogre::MovableObject*>::iterator iter = mSelected.begin();
 
 for(iter; iter != mSelected.end(); iter++)
 {
 (*iter)->getParentSceneNode()->showBoundingBox(false);
 }
}
 
void IntermediateTutorial4::selectObject(Ogre::MovableObject* obj)
{
 obj->getParentSceneNode()->showBoundingBox(true);
 mSelected.push_back(obj);
}
Compila y ejecuta la aplicacion. Ahora puedes seleccionar objetos con volumen en la escena!

Una Nota Final sobre Cajas Límites


Como habras notado en este tutorial y en los dos anteriores, la selección en Ogre se realiza mediante cajas límite de objetos mediante una consulta y no por la malla en si misma. Esto significa que un RaySceneQuery y PlaneBoundedVolumeQuery siempre aceptaran lo que la consulta golpee. Hay modos para realizar una selección perfecta de píxeles mediante rayo (que necesitas usar si estas haciendo un juego de disparos FPS) y una selección de volumen que nos dara perfectamente resultados precisos sobre la velocidad.
Desafortudamente, esto esta más allá del propósito de este tutorial. Hecha un vistazo a Raycasting a nivel de polígono para más información sobre como hacer esto en Ogre. Si tienes integrada en Ogre una librería de física, como OgreNewt, también te proporcionarán métodos para hacer esto para ti.

No aprendiestes todo sobre consultas de rayos y consultas de volúmenes. Hacer una selección basada en mallas consume mucho tiempo y puede fácilmente matar tu framerate si estas intentando comprobar todo en la escena. De hecho, el modo habitual de hacer la selección por raton es primero realizar una consulta (como con RaySceneQuery), entonces testar individualmente cada interseccion devuelta desde la consulta con un motor fisico que comprobara la geometria de la malla para ver si estas golpeando o sólo estas muy cerca.


Siguiente, Tutorial Intermedio 5 Geometría Estática