sexta-feira, 21 de outubro de 2011

Tutorial Básico 1: SceneManager, SceneNode e Entity

Esse primeiro tutorial mostra o funcionamento das classes mais básicas do Ogre 3D: SceneManager, SceneNode e Entity. Para prosseguir, é necessário ter conhecimento básico de programação, de preferência C++, mas se você conhece outra linguagem, também dá pra entender. Também é preciso que você saiba como configurar e criar um projeto Ogre no Visual Studio, o que eu já mostrei em tutoriais anteriores neste blog.

Esse tutorial foi traduzido e adaptado da Wiki do Ogre 3D. O link correspondente é: http://www.ogre3d.org/tikiwiki/Basic+Tutorial+1&structure=Tutorials

Criando um Projeto no Ogre

Antes de tudo, crie um projeto básico no Visual Studio, que mostra a cabeça do Ogre. Você pode aprender a configurar rapidamente e a criar um novo projeto através desse post.



Como o Ogre Funciona

Para continuar, vamos apresentar as três classes que são fundamentais para aplicações em Ogre.

SceneManager 
  • É o que gerencia tudo que aparece na tela 
  • O SceneManager cuida das localizações dos objetos, câmeras, luzes, planos, etc 
  • Existem vários tipos de SceneManager: os que renderizam terreno, mapas, etc 

Entity 
  • É um dos tipos de objeto que você pode renderizar numa cena 
  • Qualquer coisa que pode ser representada por uma malha 3D 
  • O Ogre não armazena a localização e a orientação junto dos objetos “renderizáveis” 
  • Isso quer dizer que você não pode colocar uma Entity diretamente numa cena 
  • Você deve anexar a Entity a um SceneNode, que possui informação de localização e orientação 

SceneNode 
  • Cuida da localização e da orientação de todos os objetos anexados a ele 
  • Uma Entity não é rederizada na cena até que você anexe ele a um SceneNode 
  • Um SceneNode não é um objeto que é mostrado na tela 
  • Uma vez que um SceneNode é criado e anexado a uma Entity (ou outro objeto), alguma coisa é mostrada na tela 
  • SceneNodes podem ter vários objetos associados a eles (ex: um personagem e uma luz ao redor dele) 
  • SceneNodes também podem ser associados a outros SceneNodes, o que permite a criação de hierarquias de nós 
  • A posição de um SceneNode é sempre relativa ao seu SceneNode pai 
  • Todo SceneNode possui um nó raiz pelo qual todos os outros SceneNodes são associados

Analisando o Código

Voltando para o projeto que nós criamos anteriormente, abra o arquivo Tutorial.cpp (ou o nome que você tenha colocado nele). Vamos analisar algumas partes do código baseado no que foi visto dessas três classes.


mSceneMgr->setAmbientLight(Ogre::ColourValue(1.0, 1.0, 1.0));


A função setAmbientLight define uma luz ambiente para a cena. Ela recebe como parâmetro ColorValue baseado em RGB. Essa função se aplica ao SceneManager (mSceneMgr).


Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

Cria uma Entity que será a cabeça do Ogro. A variável mSceneMgr possui o objeto SceneManager atual (isso é feito na classe BaseApplication). A função createEntity recebe como parâmetro um nome (deve ser único) e a malha que nós queremos usar para a entidade (ogrehead.mesh é um recurso que vem no SDK).

Observe que as malhas devem vir no formato .mesh. Se você possui algum modelo no formato .3ds ou .obj, você deve converter esses formatos para .mesh antes de poder utilizar dentro do Ogre. O Ogre oferece alguns conversores e exportadores para os programas de modelagens mais comuns neste link.


Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");

Cria um SceneNode para associar com a Entity. Toda SceneManager possui um SceneNode. Criamos então um filho para esse SceneNode, ele também possui como parâmetro um nome que também deve ser único .


headNode->attachObject(ogreHead);

Finalmente, associa a Entity ogreHead ao SceneNode.


Adicionando outro Objeto

Observe que no SceneNode criado anteriormente, nós não definimos nenhuma posição para o ogrehead.· Isso levou ao construtor de createChildSceneNode a adotar a posição padrão (0, 0, 0). Agora, nós criaremos uma segunda cabeça de Ogro, mas localizada a 100 unidades ao lado da origem.

Copie e cole o código a seguir dentro da função createScene:


Ogre::Entity* ogreHead2 = mSceneMgr->createEntity( "Head2", "ogrehead.mesh" );
Ogre::SceneNode* headNode2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "HeadNode2", Ogre::Vector3( 100, 0, 0 ) );
headNode2->attachObject( ogreHead2 );

Observe que a primeira linha cria a Entity, a segunda linha cria um SceneNode com a posição (100, 0, 0) e a terceira linha associa o SceneNode a Entity. Compile e veja o resultado!


Coordenadas e Vetores

Para prosseguirmos com a próxima parte, sobre Rotação, Translação e Escala, precisamos saber como o Ogre gerencia o espaço virtual 3D.

O Ogre usa os eixos x e y como plano horizontal e y como eixo vertical. Olhando para o seu monitor agora, o eixo x vai do lado esquerdo ao lado direito do monitor (com o lado direito sendo positivo), o eixo y vai da parte de baixo até a parte de cima do monitor (cima sendo positivo) e o eixo z vai de dentro para fora do monitor (para fora sendo positivo).



Observe que a cabeça do Ogro está de frente para você através do eixo z (isso é uma propriedade da malha e da posição da câmera). Ela se localiza na posição (0, 0, 0) 

A direção para o qual a cabeça está posicionada é um resultado de como a malha foi orientada quando foi criada e salva pelo artista  O Ogre não assume nada sobre como você orienta seus modelos (cada malha carregada pode ter um posicionamento inicial diferente) 

O Ogre usa a classe Vector para representar tanto posição quanto localização (não há classe Ponto) O Vector3 é utilizada para 3 dimensões (existem para outras dimensões: Vector2 e Vector4).


Translação, rotação e escala de Objetos 

Todas as operações relacionadas à posicionamento de objetos são feitas através de funções associadas a SceneNodes. Os SceneNodes não só definem a posição de um objeto, como também gerenciam a escala e a rotação dele.

A Translação é feita através do método translate, que recebe um Vector3 ou três números reais como entrada. Por exemplo, podemos mover a cabeça do Ogre através da função:


headNode->translate( Ogre::Vector3( 25, 0, 0 ) );

Como ela também recebe três reais como entradaa, podemos definir essa função como:


headNode->translate( 25, 0, 0 );


A Rotação é feita através do método rotate, que recebe um Vector3 como eixo de rotação, e um Radian ou Degree, que é o número de radianos ou graus que o objeto será rotacionado. Se quisermos rotacionar diretamente em torno dos eixos X, Y e Z, podemos usar os métodos pitch, yaw e roll, respecitivamente, que recebem apenas Radian ou Degree como entrada.




Vamos brincar um pouco com rotação. Crie três cabeças e rotacione elas em 90 graus em cada um dos eixos. O resultado deve ser algo como esse:


O código para fazer isto é o seguinte:


Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
headNode->attachObject(ogreHead);
 
headNode->yaw( Ogre::Degree( -90 ) );
 
Ogre::Entity* ogreHead2 = mSceneMgr->createEntity( "Head2", "ogrehead.mesh" );
Ogre::SceneNode* headNode2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "HeadNode2", Ogre::Vector3( 100, 0, 0 ) );
headNode2->attachObject( ogreHead2 );
 
headNode2->pitch( Ogre::Degree( -90 ) );
 
Ogre::Entity* ogreHead3 = mSceneMgr->createEntity( "Head3", "ogrehead.mesh" );
Ogre::SceneNode* headNode3 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "HeadNode3", Ogre::Vector3( 200, 0, 0 ) );
headNode3->attachObject( ogreHead3 );
 
headNode3->roll( Ogre::Degree( -90 ) );


A escala é feita pelo método scale(x, y, z). Vamos fazer uma escala simples com duas cabeças:


Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
headNode->attachObject(ogreHead);
 
headNode->scale( .5, 1, 2 ); 
 
Ogre::Entity* ogreHead2 = mSceneMgr->createEntity( "Head2", "ogrehead.mesh" );
Ogre::SceneNode* headNode2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "HeadNode2", Ogre::Vector3( 100, 0, 0 ) );
headNode2->attachObject( ogreHead2 );
 
headNode2->scale( 1, 2, 1 );

O resultado é o seguinte:



No próximo tutorial, aprenderemos a  manipular Câmeras, Luzes e Sombras. Até mais!

3 comentários: