9 - Vectores y Planos en el espacio
En OGRE también podemos crear nuestras propias mallas con programación. Por supuesto, esto sólo conviene para usos simples (para propósitos más complejos debemos usar software de modelado 3D, aunque siempre podemos definir a mano los 500 vértices de nuestra malla… pero a estos valientes les dejo la investigación por su cuenta: “geometría estática”). Estas mallas son “especiales”: consumen poca memoria, y no las podemos mover (excelentes para partes del escenario como rocas y montañas).
Alerta de frustración inminente: En esta guía (y en OGRE en general) se usan conceptos básicos de Geometría Euclidiana, entre otros conceptos matemáticos de nivel universitario. Si no estás dispuesto a aprender al menos sobre vectores, olvídate del desarrollo de juegos 3D para siempre. En caso contrario, antes de proseguir asegúrate de entender lo que es:
- Vector en espacio 3D.
- Vector normal a una superficie.
- Vector unitario.
- Vectores paralelos y perpendiculares.
También se pueden encontrar en internet tutoriales sobre desarrollo de juegos 3D en general, que explican estos y otros conceptos sobre vectores 3D.
Internamente, OGRE calcula los sólidos como ecuaciones matemáticas. Los sólidos básicos que tratamos se representan por ecuaciones diferenciales que les representan (Por ejemplo, el plano se representa por una ecuación de 3 variables: Ax + By + Cz = 0). Por esta razón los argumentos de las funciones pueden resultar poco “intuitivos” para personas que no hayan tenido contacto con estas matemáticas.
El eje de coordenadas en OGRE
El espacio 3D en OGRE es representado por el siguiente sistema de coordenadas (agrego la pantalla para ilustrar cómo se ve desde la cámara inicial):
Eje de coordenadas, con la cámara por defecto
Cómo se puede observar, inicialmente nuestra cámara está mirando en dirección “-Z”.
Vectores unitarios en OGRE
La clase “Vector3” representa un vector en 3D, con sus 3 componentes “X”, ”Y” y “Z”. Los vectores los usaremos ahora y siempre para representar una posición en el espacio, o para determinar una orientación (dado que los vectores apuntan en cierta dirección). En el caso de la orientación, resulta útil tener los vectores unitarios a la mano:
Vector3::UNIT_X representa un vector unitario en dirección X (1,0,0)
Vector3::UNIT_Y representa un vector unitario en dirección Y (0,1,0)
Vector3::UNIT_Z representa un vector unitario en dirección Z (0,0,1)
Las versiones negativas de dichos vectores son, respectivamente Vector3::NEGATIVE_UNIT_X, Vector3::NEGATIVE_UNIT_Y y Vector3::NEGATIVE_UNIT_Z.
Finalmente, cuando queramos definir nuestros propios vectores usaremos:
Vector3(posiciónX, posiciónY, posiciónZ)
Los 3 argumentos son números reales.
Planos
Un plano en el espacio 3D
En nuestro primer ejemplo (el cielo sin nada) agreguemos en el método CreateScene el siguiente código:
Plane plane(Vector3::UNIT_Y, 0);
Con esto preparamos un objeto de tipo “Plane”, el cual define un plano en el espacio. Los argumentos son un vector normal al plano, y una distancia inicial a la cual podemos mover el plano desde el origen, en dirección de su normal.
Luego agregamos esto para crear su malla correspondiente:
MeshManager::getSingleton().createPlane("mallaPlano",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
100,100,20,20,true,1,5,5,Vector3::UNIT_Z);
El objeto MeshManager es el encargado de administrar los recursos que corresponden a las mallas. Podemos usarlo para crear nuestros sólidos básicos. Lo usamos de esta manera:
MeshManager::getSingleton()
, para que la plantilla “Singleton” sea compilada al momento de implementar la clase (La API tiene la explicación concreta, no le veo sentido a descifrar ese jeroglífico en un tutorial para novatos).
Del MeshManager usamos el método createPlane para crear la malla del plano, con los siguientes argumentos (en orden):
- Nombre de la malla resultante.
- Nombre del grupo de recursos al cual se asignará la malla (ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME pone el nombre por defecto).
- Objeto de tipo “Plane” al cual asociaremos la malla.
- Ancho.
- Alto.
- Número de segmentos del plano en dirección X.
- Número de segmentos del plano en dirección Y.
- Crear las normales perpendiculares al plano? (true o false).
- Número de sets de coordenadas para textura.
- Número de veces que se repite la textura en dirección u.
- Número de veces que se repite la textura en dirección v.
- Un vector que representa la dirección “hacia arriba” en la superficie del plano. No puede coincidir con la normal.
No es necesario entenderlos todos por ahora; solo nos importa el nombre, ancho, alto y el vector que representa “hacia arriba” (el resto lo copiamos tal cual). Básicamente, al crear un plano en OGRE debemos tener en cuenta su dirección normal (que será su orientación), sus dimensiones (ancho y alto) y su dirección “hacia arriba”, la cual siempre debe ser perpendicular la normal (probablemente este vector sea necesario cuando agreguemos texturas uv en el plano); en caso contrario nuestro plano se deformará. Por ejemplo:
Nótese que, en el ejemplo de la imagen, la dirección “Arriba de la superficie” es perpendicular a la normal (no importa hacia donde apunte siempre que sea perpendicular).
Habiendo creado nuestra malla del plano, la asociamos a una entidad de esta manera:
Entity *ent;
ent = mSceneMgr->createEntity("EntidadPlano", "mallaPlano");
Nótese que, en vez de poner el nombre de nuestro archivo de malla, hemos puesto directamente el nombre de la malla que hemos creado en OGRE. Esta es otra manera de usar createEntity.
Finalmente, creamos un nuevo nodo de escena y le anexamos la entidad igual que siempre:
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
Compilamos y corremos el programa. Después de tanto quilombo… ni siquiera podemos ver el plano! Lo que sucede es que el plano está en el origen de coordenadas, su superficie está mirando exactamente hacia arriba (normal en dirección “Y”) y el espesor de un plano ideal es infinitamente delgado. La cámara está mirando el plano exactamente de lado (si navegas con las flechas y el mouse podrás ver el plano desde arriba).
Observa detenidamente el plano. Dale algunas vueltas. Has notado que sólo lo puedes ver si lo miras desde arriba? Si lo ves desde abajo parece que el plano no está ahí. No se trata de un error; OGRE sólo muestra una superficie si la estamos viendo desde su dirección normal. Como la normal del plano apunta hacia arriba, sólo podemos ver el plano desde arriba.
Para poder ver el plano desde el inicio de la aplicación, debemos moverlo. Podemos aprovechar el segundo parámetro del constructor de Plane para poner el plano más debajo de la cámara:
Plane plane(Vector3::UNIT_Y, -120);
El plano ya aparece al momento de correr la aplicación. Otra manera es ponerle una normal en dirección Z (ya que la cámara inicialmente está mirando en dirección -Z) para que la superficie (visible) del plano aparezca directamente hacia nosotros:
Plane plane(Vector3::UNIT_Z, 0);
MeshManager::getSingleton().createPlane("mallaPlano",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
100,100,20,20,true,1,5,5,Vector3::UNIT_Y);
Nótese que hemos cambiado la dirección “hacia arriba” de la superficie del plano para que no coincida con la normal.
También podemos poner un vector (de cualquier magnitud) que apunte en una dirección diagonal:
Plane plane(Vector3(1,1,1), 0);
MeshManager::getSingleton().createPlane("mallaPlano",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
100,100,20,20,true,1,5,5,Vector3(-1,1,1));
Esta vez he puesto una dirección “hacia arriba” que apunta en una dirección perpendicular a la nueva normal. Obviamente el cálculo de dicho vector nos complica un poco las cosas.
Probablemente la solución más fácil sea:
- Crear el plano mirando hacia arriba (esto nos facilita imaginar los vectores que deseamos).
- Asociarlo a un SceneNode.
- Rotar el SceneNode (la entidad rotará completa con él).
En general, aplicamos transformaciones como “escalar”, “rotar” y “trasladar” al SceneNode en vez de a la Entidad que se le anexa. La manipulación de SceneNodes hace parte de otro tutorial. Por ahora, actualizamos la documentación:
|
Vector3
|
|
Vector3(Real x, Real y, Real z)
|
|
Plane
|
|
Plane(Vector3 normal, Real distance)
|
|
MeshManager
|
|
MeshPtr createPlane(String name, String groupName, Plane plane, Real width, Real height, int xsegments, int ysegments, bool normals, int numTexCoordSets, Real uTile, Real vTile, Vector3 upVector)
|
Comentarios recientes
hace 5 días 21 horas
hace 5 días 21 horas
hace 6 días 3 horas
hace 1 semana 1 día
hace 1 semana 2 días
hace 1 semana 2 días
hace 2 semanas 5 horas
hace 2 semanas 1 día
hace 2 semanas 2 días
hace 2 semanas 2 días