Carga Manual de los Recursos Como cargar tus datos sin usar el formato de archivos de Ogre.


Geometría

Introducción


Hay dos modos principalmente para crear tu geometria: Usando la interfaz ManualObject(sencillo), o haciendo todo por ti mismo (obvimente mas dificil).

  • Nota1: No hablare sobre ManualObject aqui. Si quieres mas informacion sobre el, por favor visita este tutorial. Se utiliza para la creacion de objetos pequenyos (pero tambien funciona en grandes).
  • Nota2: Si no estas familiarizado con los conceptos como "bufer de vertices", "bufer de indices" o "declaracion de vertices", sera mejor que leas sobre estos temas. Este sitio es un buen lugar donde comenzar (para DirectX, pero los conceptos son los mismos).
  • Nota3: Asumire que sabes como cargar tu archivo de modelo en un formato mas leible.

Paso por paso


asi, que comencemos!

La primera cosa que necesitamos hacer es crear nuestra malla base. Para hacer esto, usaremos el metodo MeshManager::createManual:

Ogre::Mesh* mMesh = Ogre::MeshManager::getSingleton().createManual(yourMeshName, "General");


Nota: MeshManager::createManual con Ogre 1.6 ahora devuelve un Ogre::MeshPtr.

Entonces, tienes dos opciones:

  • Usar geometria compartida.
  • ... o no.

No conozco todos los pros y constras, pero se al menos dos de ellos:

  • La geometria compartida es mas eficiente si estas usando geometria estatica.
  • La geometria no compartida es necesaria si estas usando muchos huesos. Cubrire este punto mas tarde.

De cualquier modo, el proceso es en los dos casos el mismo: Crear una declaracion de vertices, crear un bufer de vertices y llenarlo con tus vertices, crear un bufer de indices y llenarlo con... indices, si. La unica diferencia es que tendras que repetir el proceso para cada submalla si no estas usando geometria compartida.

Ahora los detalles:

Sub-malla


Todos los modelos tienen submallas (No se si seria posible no tenerlas). Pueden ser consideradas como "partes del modelo" (una rueda del coche, una arma enemiga...).
La creacion es muy facil: Usaremos el metodo Mesh::createSubMesh:
Ogre::SubMesh* mSubMesh = mMesh->createSubMesh(yourSubMeshName);
Nota: usa MeshPtr.get() para devolver la malla desde el MeshPtr.

yourSubMeshName (el nombre de la submalla) no tiene que ser unico. Pero es recomendable. Si no quieres darle nombre usa:
Ogre::SubMesh* mSubMesh = mMesh->createSubMesh();
Creara una Submalla con un valor de "index" para accederla. El "index" comienza en 0 y es incrementado cada vez que creas una nueva SubMalla (incluso con el primer metodo).

Declaración de Vértices


La declaracion de vertices es una parte esencial de la carga de la geometria: dice a la tarjeta grafica que clase de informacion esta disponible para cada vertice (posicion, normal, coordenada de textura, color, ...).
Ogre ofrece una interfaz simple para crear esta declaracion: VertexDeclaration. Asi es como creamos una:

// Primero tenemos que crear un VertexData
Ogre::VertexData* data = new Ogre::VertexData();
// Entonces, lo enlazamos a nuestra Malla/SubMalla :
#ifdef SHARED_GEOMETRY
      mMesh->sharedVertexData = data;
#else
      mSubMesh->useSharedVertices = false; Este valor es "true" por defecto
      mSubMesh->vertexData = data;
#endif
// Tenemos que proporcionar el numero de verices que pondremos en esta Malla/SubMalla
data->vertexCount = iVertexCount;
// Entonces podemos crear nuestra Declaracion de vertices
Ogre::VertexDeclaration* decl = data->vertexDeclaration;

Estamos listos para meter algunos datos!, usaremos VertexDeclaration::addElement:

decl->addElement(0, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);

Aqui estamos: le decimos a OGRE que nuestros vertices estaran compuestos por 3 valores float, que Ogre debe interpretar como una posicion.
Y que? si queremos mas? Si queremos que nuestra Malla se renderice adecuadamente, tendremos que proporcionar informacion sobre sus normales (Son usadas para la iluminacion). Asi es la manera:

size_t offset = 0;
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);

Como habras notado, hemos introducido una nueva variable: offset. Esta se usa para decirle a Ogre que normales seran colocadas desplazadas despues de la direccion de vertices.

bien, ahora que hemos comprendido el concepto, anyadimos otro elemento: las coordenadas UV (coordenadas de textura). Ya que hay una sintaxis especial que debe ser usada cuando anyadimos multiples elementos a la misma semantica solo ilustraremos esto anyadiendo dos conjuntos de coordenadas de texturas (mirar nota 2, debajo). Facilmente:

size_t offset = 0;
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
decl->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
decl->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 1);

Bien! Pero hay una pequenya cosa con la que debes tener cuidado si quieres animar tu malla despues: Skinnig por software (y quizas animacion de vertices) se necesita que estas posiciones y normales esten contenidas en un bufer separado de los otros elementos.


Y entonces? Bien, en la ultima declaracion de vertices, le digimos a Ogre que todo estaba en el mismo bufer. Como se esto? Mira al primer argumento de addElement: "0". Es el indice del bufer de vertices en el que Ogre buscara tu elemento. Cambiemos esto:

size_t offset = 0;
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
 
decl->addElement(1, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);

Esto es mejor. Como habras notado, el desplazamiento (offset) esta ajustado a 0 cuando usas un bufer diferente.


Nota 1: Los pesos de mezclado e indices se explicaran mas tarde.


Nota 2: Si quieres crar dos elementos con la misma semantica (el mismo "role"(papel)), debes usar el argumento opcional de addElement. Es un indice que tienes que incrementar cada vez que creas un nuevo elemento que ha sido usado. Si omites este parametro, como hicimos para el primer conjunto de coordenadas de textura, sera por defecto 0. El segundo conjunto lo ponemos a "1".


Búferes de Vértices



Como te dije en la seccion anterior, crearemos dos buferes de vertices:
Uno para las posiciones y normales y otro para las coordenadas UV. Usa HardwareBufferManager::createVertexBuffer:

Ogre::HardwareVertexBufferSharedPtr vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(
decl->getVertexSize(0), // Este valor es el tamanyo del vertice en memoria
iVertexNbr, // El numero de vertices que pondras en este bufer
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY // Propiedades
);
Ahora, hay varios modos de escribir datos en tu nuevo bufer:

  • El primero: llamar a HardwareBuffer::lock y usar el puntero devuelto para la entrada de tus datos.
  • El segundo: Copia solamente un array existente usando HardwareBuffer::writeData:

vbuf->writeData(0, vbuf->getSizeInBytes(), array, true);
De cualquier modo, necesitaras escribir tus datos en un orden particular: el que has proporcionado en la declaracion de vertices:
array[0] = vertices[1].x
array[1] = vertices[1].y
array[2] = vertices[1].z
array[3] = vertices[1].normal.x
array[4] = vertices[1].normal.y
array[5] = vertices[1].normal.z
array[6] = vertices[2].x
...
Ahora que este bufer esta listo, todavia necesitamos enlazarlo a su Malla/SubMalla! Asi es como se hace:
// "data" es el Ogre::VertexData* que hemos creado antes
Ogre::VertexBufferBinding* bind = data->vertexBufferBinding;
bind->setBinding(0, vbuf);

Bien! Ahora haremos lo mismo con las coordenadas UV. Actualmente solo hay dos diferencias: necesitamos cambiar decl->getVertexSize(0) a decl->getVertexSize(1) en el metodo createVertexBuffer y bind->setBinding(0, vbuf);vendra a ser enlazado bind->setBinding(1, vbuf);.


"0" fue para el primer bufer, "1" si es para el segundo, "2" seria para el tercero, ...


Búfer de índices



Ahora para el ultimo: El bufer de indice no funcionara de la misma forma como los buferes de vertices funcionan. Tenemos varias opciones:


  • Si estamos usando geometria compartida, entonces podemos:
    • crear un bufer de indice por SubMalla....
    • ... o crear un indice global de bufer.
  • sino si no estamos usando geometria estatica, tendremos que crear un bufer de indice por SubMalla.


Asi es como lo creamos, usando HardwareBufferManager::createIndexBuffer:
Ogre::HardwareIndexBufferSharedPtr ibuf = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
Ogre::HardwareIndexBuffer::IT_16BIT, // Puedes usar varios valores diferentes aqui
iIndexNbr, // El numero de indices que pondrias en ese bufer
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY // Propiedades
);
Antes de decirte como escribir mas datos, veremos como OGRE enlaza los buferes de indice a sus SubMallas:
mSubMesh->indexData->indexBuffer = ibuf; // El puntero al bufer de indice
mSubMesh->indexData->indexCount = iIndexNbr; // El numero de indices que usaremos
mSubMesh->indexData->indexStart = 0; // El desplazamiento desde el comienzo del bufer de indice

Porque necesitamos un desplazamiento? Si estas usando un bufer de indice global, entonces necesitaras mezclar varias SubMallas en el mismo bufer:

index[0] = subMesh[0].vertexIndices[0]
index[1] = subMesh[0].vertexIndices[1]
index[2] = subMesh[0].vertexIndices[2]
 
index[3] = subMesh[1].vertexIndices[0]
index[4] = subMesh[1].vertexIndices[1]
index[5] = subMesh[1].vertexIndices[2]
...

subMesh[0] tendra un desplazamiento de "0", pero subMesh[1] tendra "3". Ambos de ellos tendran una cuenta de indice de "3".


Si no estas usando un bufer de indice global, entonces el desplazamiento siempre sera "0".


Al menos, necesitaremos rellenar algunos datos, usando el mismo metodo que escogistes para los buferes de vertice.


Ahora que toda nuetra geometria esta cargada, todavia necesitaremos hacer unas pocas cosas antes de que podamos usar nuestra Malla:


Caja de límite y esfera



No estoy seguro, pero pienso que es lo necesario para que Ogre realice un recortado preciso (no renderice objetos que no estan visibles en la pantalla).


Si estas usando geometria estatica, entonces este paso es muy facil: cuando les tus vertices desde tu archivo, solo guarda los valores mayor y menor de cada eje (xMax, xMin, yMax, yMin, zMax, zMin). Entonces:

mMesh->_setBounds(Ogre::AxisAlignedBox(xMin,yMin,zMin,xMax,yMax,zMax));
mMesh->_setBoundingSphereRadius(std::max(xMax-xMin, std::max(yMax-yMin, zMax-zMin))/2.0f);

De cualquier forma, la mejor opcion es incluir esta informacion directamente en tu archivo de modelo.


Un último paso



Llamar a:

mMesh->load();


Lo hicistes :-)


Conclusión



Aqui esta un pequenyo diagrama que muestra a grandes lineas la carga manual de geometria:


Geometria compartida:


  • Crea tu objeto Mesh(Malla)
  • Crea la declaracion de vertices
  • Crea el bufer de vertices diferente (separa [posicion+normales] del resto si vas a animar la malla)
  • Enlazalos a tu Malla
  • Crea tu bufer de indices
  • Crea tus SubMallas y enlazalas al bufer de indice
  • Llama a Mesh::Load()


Geometria no compartida:


  • Crea tu objeto Mesh
  • Para cada SubMesh:
    • Crea la declaracion de vertice
    • Crea tus buferes de vertices diferentes (separa [posicion+normales] del resto si vas a animar la malla)
    • Enlazalos a tu SubMalla
    • Crea tu bufer de indice
    • Enlazalo a tu SubMalla
  • Llama a Mesh::load()






Material

Introduccion



Todavia estoy aprendiendo este tema. Se lo muy basico, pero edito esto porque pudiera resultar util.


Paso a paso



Crear un Material comienza del mismo modo que crear una Malla:

Ogre::MaterialPtr mMat = Ogre::MaterialManager::getSingleton().create(yourMaterialName, "General", true);

Entonces, todo es bastante directo:


Tecnica



El Material que hemos creado solo comienza con una Technique.


Si necesitas mas, necesitaras crear una usando Material::createTechnique:

Ogre::Technique* mTech = mMat->createTechnique();

Pasada



Cada Technique que creamos (incluida el proporcionada automaticamente con el Material) comienza con una Pass (Pasada).


Si necesitas mas de una, puedes usar Techique::createPass:


Ogre::Pass* mPass = mTech->createPass();

TextureUnitState



Tendremos que crear un TextureUnitState cada vez que tenemos que usar una textura. Pasar los objetos no se hara automaticamente con la creacion, asi que aqui haremos esto, usando Pass::createTextureUnitState:

Ogre::TextureUnitState* mTexUnitState = mPass->createTextureUnitState();

Añadiendo un poco de color



Ahora que sabemos como crear las diferentes partes de un Material, veremos como anyadir algo util: color difuso.


Esta es una propiedad del objeto Pass, asi usaremos Pass::setDiffuse:

mPass = mMat->getTechnique(0)->getPass(0);
  mPass->setDiffuse(Ogre::ColourValue(red, green, blue, alpha));


¿Cómo hacer, con la Textura?



Esta parte es un poco mas dura, primero necesitamos crear una Textura usando TextureManager::load:

Ogre::TexturePtr mTex = Ogre::TextureManager::getSingleton().load(yourTextureFileName, "General");

Entonces, necesitamos crear un TextureUnitState para nuestra Pass y enlazar la textura a el:

mPass->createTextureUnitState()->setTextureName(mTex->getName());


Uso



Ahora, estamos contentos: Tenemos un Material con una bonita textura. Pero, como lo usamos?


Los materiales se asignan a SubMallas. La asignacion es muy facil:

mSubMesh->setMaterialName(yourMaterialName);

... y eso es todo! Puedes tambien usar el nombre de un Material que cargues en el archivo .material.


Es bueno saber



Si quieres usar tu Material para un componente 2D (como un overlay, un sprite, etc), necesitaras hacer unos pocos ajustes para renderizarlos apropiadamente en la pantalla.

mPass->setCullingMode(Ogre::CULL_NONE); // NO ocultar (se pueden ver ambas caras de los triangulos)
mPass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); // Permite transparencia del canal alfa (tga, png, ...)
mMat->setLightingEnabled(false); // Desactiva la iluminacion
mMat->setDepthCheckEnabled(false); // Sin comprobacion de la profundidad

(Deberias usar unas pocas de estas cosas dependiendo del caso)


Preguntas


Es el metodo load() necesario ? Cuando deberia ser llamado?



Skeleton (Esqueleto)


Introduccion



Aqui viene la parte mas dificil...


Como para la carga de geometria, necesitaras unos pocos prerequisitos antes de nada:
Recomiendo leer este ppapel (otra vez, solo para DirectX).


Paso por Paso



Como cada recurso fue creado desde ahora, un Skeleton necesita ser creado desde el manager apropiado:
Ogre::Skeleton* mSkel = Ogre::SkeletonManager::getSingleton().create(yourSkeletonName, "General", true);

Ahora, dependiendo del modo en que quieras jugar con tu skeleton, puedes decidir usar el sistema de animacion de OGRE o actualizar tus huesos tu mismo ( para ragdolls o si quieres mas flexibilidad).


Hueso



De cualquier manera, una vez que nuestro Skeleton se ha creado, necesitaremos crear unos pocos huesos con
Ogre::Bone* mBone = mSkel->createBone();

Todavia, si creas todos los huesos con este metodo, seran huesos raiz(no tendran padre).

Para crear la relacion padre-hijo, tienes dos modos:

Ogre::Bone* mParent; // Has creado esto antes
Ogre::Bone* mBone = mSkel->createBone();
mParent->addChild(mBone);
... o:
Ogre::Bone* mParent; // Has creado esto antes
Ogre::Bone* mBone = mParent->createChild()

No hay absolutamente ninguna diferencia entre los dos. El resultado es el mismo. Pero a veces necesitaras crear todos tus huesos primero y entonces enlazarlos juntos. Usa mejor el primer metodo en este caso.


Posicion: Cada hueso tiene una posicioin inicial que difine la pose por defecto.
Nota que Ogre::Bone deriva de Ogre::Node, asi su posicion es relativa a su padre.
Para establecer una posicion de hueso, puedes usar Bone::setPosition:

Ogre::Vector3 mPos(0.0f, 0.0f, 0.0f);
mBone->setPosition(mPos);

Orientacion: La mayoria del tiempo, las animaciones estan hechas de rotaciones.
Aqui, puedes proporcionar este hueso como base de orientacion usando Bone::setOrientation:

Ogre::Quaternion mRot(1.0f, 0.0f, 0.0f, 0.0f);
// Ten cuidado : Los quaterniones de Ogre son (w,x,y,z)
mBone->setOrientation(mRot);
Escala: como las dos anteriores, usando Bone::setScale :
Ogre::Vector3 mScale(1.0f, 1.0f, 1.0f);
mBone->setScale(mScale);
Unos pocos detalles: Ahora, si estas planeando manejar tus huesos tu mismo, necesitaras establecer un parametro muy importante:
mBone->setManuallyControlled(true);
... y, dependiendo del modo en que quieras manejar tus animaciones, puedes llamar:
mBone->setInitialState();
Esto le dira a Ogre que el parametro actual Bone's (posicion, rotacion, escala) se guardara como una base para todas las futuras transformaciones.
Si no estas usando el sistema de animacion de Ogre, entonces puedes devolver este estado inicial llamando a:
mBone->resetToInitialState();
Si no, Ogre lo hace automaticamente.

Animación


Si no quieres usar el sistema de animacion de OGRE, entonces no necesitas leer esta parte. Asumire que sabes lo que estas haciendo, y que sabes como manejar tus propios datos.

Solo te mostrare como aplicar tus transformaciones a tu Bone:
mBone->setScale(scale);
mBone->setOrientation(rotation);
mBone->translate(mLastTrans*(-1));
mBone->translate(translation);
La linea numero 3 es necesaria si tu traslacion es relativa a la posicion inicial del Bone (mLastTrans es la translacion que tienes que aplicar a tu hueso en la actualizacion anterior).

Sino, haremos algo de abstraccion:

Digamos que tienes dos animaciones para tu modelo ("stand"(parado) y "walk"(andando)). Ogre necesitara que crees una pista de animacion para cada hueso y para cada animacion.

Asi, que basicamente tendras esto:

  • Bone[0] :
    • Crea la pista de animacion para "stand"
    • Crea la pista de animacion para "walk"
  • Bone[1] :
    • ...

No se, pero lo he usado para tener solo una "pista (o "linea de tiempo") por hueso, asi que lo encuentro un poco confuso.
De cualquier modo, creemos todo esto!

Creacion de la Animacion: Antes de cargar tus datos de animacion, tendras que asegurarte que quieres crear tus diferentes animaciones.
Para hacer esto, usaremos Skeleton::createAnimation:
Ogre::Animation* mAnim = mSkel->createAnimation(yourAnimationName_uniqueForThisModel, yourAnimationLength_inSeconds);
Sencillo

Las pistas de animacion: De acuerdo, ahora volveremos a nuestros Bones!
Para cada animacion, necesitaras crear una pista de animacion. Que es un contenedor que asocia una llave (de tiempo) y un valor (la transformacion). Hay varios tipos de pistas de animacion, pero para animacion esqueletica, solo necesitamos una: el NodeAnimationTrack, que puede ser creado usando Animation::createNodeTrack:
Ogre::NodeAnimationTrack* mTrack = mAnim->createNodeTrack(yourTrackHandle_uniqueForThisAnimation, mBone);
Ahora que se ha creado, podemos llenarlo con algun TransformKeyFrames que tendremos creado previamente usando NodeAnimationTrack::createNodeKeyFrame:
Ogre::TransformKeyFrame* mKey = mTrack->createNodeKeyFrame(time);
Puedes entonces proporcionar todas las transformaciones:
Ogre::Vector3 translate(0.0f,0.0f,0.0f);
mKey->setTranslate(translate);
 
Ogre::Vector3 scale(1.0f,1.0f,1.0f);
mKey->setScale(scale);
 
Ogre::Quaternion rotation(1.0f,0.0f,0.0f,0.0f);
mKey->setRotation(rotation);
Nota: Si no proporcionas una transformacion, Ogre usara la matriz identidad (o una traslacion zero, una escala de 1x o una rotacion 0).

Ejemplo:

  • Key[0]
    • Translation = 0, 0, 5
    • Scale = 1, 1, 0.5
    • Rotation = 1, 0, 0, 0
  • Key[1]
    • Translation = 0, 0, 15
    • Rotation = 1, 0, 0, 0

... Aqui, no has proporcionado la escala para la segunda llave. Si asumes que Ogre usara una desde la llave anterior, estas equivocado!
La Key[1] se parecera a esto:

  • Key[1]
    • Translation = 0, 0, 15
    • Scale = 1, 1, 1 <--- escala identidad, parametro por defecto
    • Rotation = 1, 0, 0, 0

Terminando: De acuerdo, ahora que tenemos un Esqueleto, con varias Animaciones que hemos enlazado a los Huesos usando NodeAnimationTrack. Bien!
Tenemos que hacer una cosa antes de que todo este hecho: Enlazar el Esqueleto a la malla.
Usaremos Mesh::_notifySkeleton:
mMesh->_notifySkeleton(mSkel);
Eso es todo.

Ahora, si has usado todo el esqueleto y los huesos, debes pensar: Que todavia nos falta algo.

Y estas en lo cierto: No enlazamos nuestro esqueleto a nuestros vertices! (_notifySkeleton no lo hace automaticamente...). Veremos como en el siguiente capitulo.

Skinning


Nota: Esta seccion no trata con Skinning, pero con Skinning.

En el jargon 3D, skinning es la accion de enlazar un esqueleto a la malla 3D y sus vertices.

Dependiendo de tu formato del modelo, es una tarea dificil, o termina tan simple como crear un nuevo Material.
La animacion esqueletica es un proceso pesado. Puedes hacerlo en software (calculos hechos en CPU) o en hardware (calculos hechos por la GPU). El primer modo es el mas simple de implementar, con muy pocas limitaciones, pero tambien es el metodo mas lento. El ultimo modo es mucho mas rapido, pero tiene que comunicar con tu tarjeta grafica (con sombreadores) y este hecho impone limitaciones.

La version de DirectX se sombreadores 2.0 y 3.0 permiten 256 constantes float4, lo que se traslada a matrices de 64 por vertex (256 es el minimo permitido por 2.0 y 3.0, y puede variar dependiendo de la tarjeta grafica).

Si nunca has jugado con los sombreadores, pensaras que "64 matrices para un unico vertice es demasiado!". Es cierto, un vertice usa 5 matrices (4 para animacion esqueletica, y 1 para world/view/projection). De todas formas, no deberias intentar mandar 5 matrices por vertice a tu tarjeta grafica. Enviar datos a la tarjeta grafica es costoso. Asi que tienes que mandar todo en un archivo por lote, todo lo que puedas.

El metodo que es elegido la mayoria del tiempo es dividir el Esqueleto en una lista de huesos locales para cada SubMalla. Actualmente, Una SubMalla no usa todo el Esqueleto (ej: Mis modelos nunca usan mas de 60 matrices por SubMalla, y es un numero bastante alto).

Asi, si tu formato de modelo ya contiene una lista de huesos locales y toda la informacion necesaria. Pero sino, estaras obligado a crear esta lista en tiempo de ejecucion.

Actualmente, no he usado el skinning por hardware. Asi que no se como Ogre maneja todo eso. Editare esta parte cuando sepa mas, pero si lo conoces, edita este articulo.

De cualquier modo, Los huesos y sus pesos seran guardados en un bufer de vertice, solo como posicion, normales y coordenadas UV. Ogre ofrece un modo cool de hacer esto sin crear manualmente los buferes: VertexBoneAssignment.

Mejor creamos solo despues de nuestros buferes de vertices de SubMalla.
VertexBoneAssignement se hace por vertice:
for (int i = 0; i < iVertexNbr; i++)
{
   Ogre::VertexBoneAssignment vba;
   // El indice del vertice en el bufer de vertice
   vba.vertexIndex = static_cast<unsigned int>(i);
   for (int j = 0; j < MAX_BONE_PER_VERTEX ; j++)
   {
       // El indice del hueso que queremos usar
       vba.boneIndex = bones_ind[i][j];
       // El peso que queremos dar al hueso (la suma de todos los pesos para un unico vertice debe ser 1.0f)
       vba.weight = bones_wgt[i][j];
       mSubMesh->addBoneAssignment(vba);
   }
}
 
// Aplicar cambios, construir el bufer
mSubMesh->_compileBoneAssignments();
El problema aqui, es que estamos usando indices de hueso global: Que son directamente referenciados a huesos en el esqueleto. Asi si incluso usas skinning por hardware, estaras en problemas si tienes muchos huesos.

Se que tienes que hacer algo con Model::blendIndexToBoneIndexMap, pero no estoy seguro de como usarlo. A juzgar por la descripcion dada en los documentos, Se ve que ogre lo crea en si mismo cuando compilas VertexBoneAssignements. Quizas no haya nada que hacer...

Nota Auxiliar


Ahora que todo esta listo, hay una pequenya cosa que tenemos que tener en mente si quieres animar tus huesos.

Cuando creas una Entity con tu Malla, Ogre olvida la bandera setManuallyControlled que establecistes antes! Necesitaras reinstalarla a true para cada hueso, sino tu Entity no se animara.

Para hacerlo asi, necesitaras conseguir la SkeletonInstance (la instancia de esqueleto) creada para tu Entity:
Ogre::SkeletonInstance* mSkel = mEntity->getSkeleton();
... y actualizar la bandera para cada hueso:
for (int i = 0; i < iBoneNbr; i++)
 {
     mSkel->getBone(i)->setManuallyControlled(true);
 }
Deberias entonces estar listo para jugar con tu modelo animado!

Fuente

Introduccion


El objeto fuente es usado para mostrar algun texto en la pantalla, y guarda datos sobre la forma de tus letras.

Hay dos tipos de fuentes:
fuentes de imagen: Necesitan de un archivo de textura (un .jpg, .png u otro) o cada letra que es dibujada despues de otra.

  • Pros: muy rápido, permite formas de letra sin limites (incluyendo grosores, etc).
  • Contras: se estropean al escalarse (aumentar o disminuir).

Las fuentes TrueType (OpenType funcionan tambien): Estan basadas en un fichero de fuente (.ttf), que contiene los graficos de vector.
Una cosa que notar: la libreria que Ogre usa (FreeTyoe) no dibuja tus letras en grafico de vector (podria ser demasiado costoso). Actualmente, se rasteriza el grafico de vector en una textura, lo que eventualmente se usa como una imagen de fuente.

  • Pros: permite el uso de tamanyos ilimitados mientras se mantiene la misma calidad (usando solo un archivo).
  • Contras: sufre de la misma limitacion que todas las fuentes TrueType (solo un color).

Nota: no puedes cambiar el tamanyo de la fuente una vez se ha creado.

Actualmente, el tipo de fuente solo define el modo de cargarlo en OGRE. Terminaras usando las mismas cosas: una textura que contenga todas las letras (o las que elijas).

Ambos metodos de carga seran cubiertos.

Paso a paso


Como todo en este tutorial, hemos creado un objeto Font usando el FontManager:
Ogre::FontPtr mFont = Ogre::FontManager::getSingleton().create(yourFontName_unique, "General");
Entonces viene la eleccion de cargar tu Font con una textura:
mFont->setType(Ogre::FT_IMAGE);
... o con un archivo .ttf:
mFont->setType(Ogre::FT_TRUETYPE);
Da igual que escojas, tendras que decirle a Ogre que archivo cargar:
mFont->setSource(source)
Si has escogido una fuente de imagen, entonces el fuente sera la ruta a la textura. Sino, si has escogido la fuente TrueType, entonces la fuente sera la ruta al archivo .ttf.

Entonces, tendremos que dividir el tutorial en dos partes porque el proceso de carga es completamente diferente:

Imagen de Fuente


Ahora, Ogre tiene una textura rellena de cosas. Como sabe donde encontrar el caracter "A"?
El no lo sabe :-) Bien, no hasta que se lo digamos, donde esta localizado cada caracter individual...
Asi que, tendras que llamar al metodo Font::setGlyphTexCoords para cada caracter:
Ogre::Font::setGlyphTexCoords(id, u1, v1, u2, v2, textureAspect);
  • id es el codigo de caracter (33, 255, ...) que quieres anyadir.
  • u1 y v1 son las coordenadas de la esquina izquierda arriba de la letra correspondiente.
  • u1 y v2 son las coordenadas de la esquina inferior izquierda.
  • textureAspect ... bien no lo se: textureAspect es el ancho/alto de la textura (deberia no ser un cuadrado). Porque tiene que ser establecido para cada caracter si este valor es el mismo para todos?

De cualquier manera, tu objeto Font esta ahora listo para ser usado.
Nota: Nunca he intentado esto, asi que puedo estar equivocado. Si alguien ha usado fuentes de textura antes, por favor que lo confirme.

Fuentes TrueType


La primera cosa que necesitamos definir es la calidad de la textura producida.
Para hacer esto, Ogre proporciona dos funciones:
mFont->setTrueTypeSize(size);
mFont->setTrueTypeResolution(resolution);
En este momento, no se exactamente como usarlos. Pero puedo decirte esto:

  • el tamanyo deberia ser el tamanyo de la fuente que este planeando usar con el objeto Font (ejemplo: 16pts).
  • la resolucion define la calidad de tu fuente (puntos por pulgada...). He leido algo de que un valor de 96 es bueno para un uso basico ( no importa como de grande se renderice tu texto).

Entonces, la ultima cosa que tenemos que hacer es seleccionar que caracteres usaremos:
mFont->addCodePointRange(Ogre::Font::CodePointRange(33, 255));
Esto se dice a Ogre que caracteres usaremos para los numeros del 33 al 255 (esto incluye a las letras de acentuacion como é,
, ñ, ’, ..., todas las marcas de puntuacion comunes, signos y numeros).
Si quieres usar todo el rango [33,255], pero no quieres el caracter 125, entonces tendras que llamar lo dos veces:
mFont->addCodePointRange(Ogre::Font::CodePointRange(33, 124));
mFont->addCodePointRange(Ogre::Font::CodePointRange(126, 255));
Nota: que ogre usa automaticamente el rango [33, 166] si no se le proporciona.

Eso es todo!

Superposicion

Introduccion


La creacion de Superposiciones es tan sencilla que se puede cubrir en una sola seccion. El objeto OverlayElement.

Este tema es cubierto aqui, pero quiero tenerlo todo en un solo lugar. Basicamente, encontraras en esta seccion que estamos haciendo exactamente la misma cosa que en el articulo. La unica diferencia es que lo documentare un poco mas en cada paso.

Paso a pasos
Superposicion

Como mencione anteriormente, la creacion se Superposiciones es realmente muy facil. Si has leido el articulo entero hasta ahora, entonces lo siguiente no te sorprendera:
Ogre::OverlayManager* mOverlayMgr = Ogre::OverlayManager::getSingletonPtr();
Ogre::Overlay* mOverlay = mOverlayMgr->create(yourOverlayName_unique);
Entonces, como para elemento GUI, un Overlay (superposicion) tiene los metodos show() y hide(). Deberias notar que cada Overlay solo se crea (o carga desde un archivo .overlay) esta oculto por defecto. Asi, si quieres que su contenido aparezca por pantalla, tendras que llamar a show().

Pero, solo haz esto cuando hayas cargado tu jerarquia GUI entera! show() automaticamente llamara a initialize() en la superposicion y su hijo (si no esta ya inicializado).

La unica otra cosa que debes hacer con tu Overlay es cambiar su valor Z. Nosotros haremos esto ahora:
mOverlay->setZOrder(500);
Nota: el valor Z es capado a 650 (por defecto es 100).


OverlayElement


Una Superposicion no es una parte grafica de GUI. Es solo un contenedor base en el que pondremos algunos OverlayElements.

Para crear un OverlayElement, deberemos usar OverlayManager otra vez:
Ogre::OverlayElement* mElement = mOverlayMgr->createOverlayElement(yourElementType, yourElementName_unique);
Tiene varios metodos para posicionarlo y escalarlo en la pantalla, pero OGRE puede interpretar nuestras llamadas de dos modos diferentes:

Antes de llamar a cualquier cosa, debemos definir cuales de estos pasos queremos:
mElement->setMetricsMode(mode);

el modo puede ser:

  • Ogre::GMM_PIXELS (coordenadas absolutas)
  • Ogre::GMM_SCREEN (coordenadas relativas de pantalla)

Ademas, tambien tenemos algunos metodos para decirle a Ogre que punto estamos considerando como la posicion padre. Por defecto, es la esquina superior izquierda de nuestro elemento padre (solo funciona con Ogre::GMM_PIXELS ?). Pero cargaremos esto:
mElement->setVerticalAlignment(Ogre::GVA_BOTTOM);
mElement->setHorizontalAlignment(Ogre::GHA_RIGHT);
Ahora nuestro punto de elemento de anclaje esta en la esquina inferior derecha de su padre.

Asi, los otros dos metodos necesarios seran:
mElement->setPosition(left, top);
Nota: Si el modo es Ogre::GMM_SCREEN, entonces setPosition(1,1) pondra nuestro elemento en la esquina inferior derecha de la pantalla. Sino, desplazara nuestro elemento desde la esquina superior izquierda de la pantalla (en pixeles).
mElement->setDimensions(width, height);
Nota: Si modo es Ogre::GMM_SCREEN, entonces setDimension(1,1) hara que nuestro elemento tenga el mismo tamanyo de nuestra pantalla. Sino, simplemente se establecera el numero de pixel de nuestro elemento width (ancho) y height(alto).

Entonces, lo mas interesante:
mElement->setMaterialName(yourMaterialName);
Puedes asignar un material a tu elemento! Que te permitira mostrar una textura, o rectangulo de color plano.

Una vez que nuestro elemento esta creado, deberiamos enlazarlo al Overlay:
mOverlay->add2D(mElement);
Bien, eso es todo!.

Ahora, debes pensar: "Hum, es muy basico". Es cierto!

Pero el OverlayElement es actualmente una clase virtual de la que tiene otras herencias:

  • TextAreaOverlayElement
  • OverlayContainer

Tambien tienen sus propios metodos, y se describiran aqui:
TextAreaOverlayElement: Este es usado para mostrar algun texto en la pantalla (usando un objeto Font).
Ogre::TextAreaOverlayElement* mTextArea = static_cast<Ogre::TextAreaOverlayElement*>(
mOverlayMgr->createOverlayElement("TextArea", yourElementName_unique)
);
La primera cosa que queremos hacer es establecer la Font que usaremos:
mTextArea->setFontName(yourFontName);
... entonces, el tamaño con el que queremos que nuestro texto se dibuje:
mTextArea->setCharHeight(16);
... Podemos tambien jugar con el color del texto:
mTextArea->setColour(Ogre::ColourValue(0.3, 0.5, 0.3));
... y eventualmente establecer un titulo (el texto a renderizar):
mTextArea->setCaption("Hello OGRE!");
OverlayContainer: La particularidad de este elemento es que puede contener otros elementos!
Util cuando quieres agrupar tu elemento en una jerarquía.
Ogre::OverlayContainer* mPanel = static_cast<Ogre::OverlayContainer*>(
mOverlayMgr->createOverlayElement("Panel", yourElementName_unique)
);
Puedes añadir un elemento hijo de forma bastante simple:
mPanel->addChild(mTextArea);
Entonces, si mueves mPanel, mTextArea seguira.

¿Descarga?


No se todavia como deberia asegurar una descarga apropiada. Si tienes algun consejo, por favor compartelo !