Ponto V!

Home Matemática e Física Utilizando Motores de Física - Integrando Bullet com Ogre3d
Bruno Crivelari Sanches
Utilizando Motores de Física - Integrando Bullet com Ogre3dImprimir
Escrito por Bruno Crivelari Sanches
Índice do Artigo
Utilizando Motores de Física
Criando um Programa Exemplo
Integrando Bullet com Ogre3d
Configurando o Projeto e Download
Todas Páginas

Integrando Bullet com Ogre3d

A integração de ambos é relativamente simples, precisamos a principio apenas atualizar a posição dos SceneNodes do Ogre com base nos objetos físicos. Lembre-se que agora a Bullet é quem vai cuidar de movimentar os objetos da cena, precisamos então informar ao Ogre as novas posições dos objetos.

Para fazer esse trabalho vamos criar uma classe chamada SceneObject, que vai possuir referência para um SceneNode e para um RigidBody e um método para sincronizar a transformação do corpo rígido com o SceneNode, o código pode ser conferido a seguir:

class SceneObject
{
    private:
        Ogre::SceneNode &mNode;
        btRigidBody &mRigidBody;

    public:
        SceneObject(Ogre::SceneNode &node, btRigidBody &body):
          mNode(node),
          mRigidBody(body)
        {
            //empty
        }
        
        void prepareToRender()
        {
            btTransform transform;

            mRigidBody.getMotionState()->getWorldTransform(transform);

            btQuaternion rotation(transform.getRotation());

            mNode.setOrientation(rotation.getW(), rotation.getX(), rotation.getY(), rotation.getZ());

            const btVector3 &origin = transform.getOrigin();
            mNode.setPosition(origin.getX(), origin.getY(), origin.getZ());
        }
};

A parte inicial da classe e seu construtor não trazem grandes complicações: temos dois atributos, sendo uma referência para o SceneNode e outra para o RigidBody. No construtor apenas inicializamos ambos os atributos.

A mágica mesmo ocorre no método prepareToRender, que iremos chamar toda vez que for preciso desenhar a tela. O método primeiramente cria um btTransform (que como você deve se lembrar é a classe da Bullet que armazena transformações). Na sequência acessamos o btMotionState do corpo rígido e copiamos sua transformação para coordenadas do mundo, como mostrado abaixo:

btTransform transform;

mRigidBody.getMotionState()->getWorldTransform(transform);

Com a transformação do corpo rígido vamos então copiar seu Quaternio e alterar a transformação do SceneNode, isso é feito no código a seguir:

btQuaternion rotation(transform.getRotation());

mNode.setOrientation(rotation.getW(), rotation.getX(), rotation.getY(), rotation.getZ());

Por fim, vamos pegar a posição do corpo rígido e refletir essa posição no SceneNode, como mostrado abaixo:

const btVector3 &origin = transform.getOrigin();
mNode.setPosition(origin.getX(), origin.getY(), origin.getZ());

E com esta pequena classe já tornamos possível manter um SceneNode “sincronizado” com um corpo rígido. Se para cada objeto da cena que tiver um corpo rígido criarmos uma instância da classe SceneObject e invocarmos o método prepareToRender sempre antes de desenharmos os objetos, vamos manter os objetos sempre sincronizados, vamos ver como fazer isso no próximo item.

Criando uma Cena de Teste

Como já foi mencionado, iremos utilizar o código fonte do artigo “Primeiro programa com Ogre 3d”, bastando a principio incluir as novas classes que foram criadas ao longo deste artigo. Agora começaremos a modificar o código já existente.

Vamos então adicionar na classe Tutorial1 a declaração do PhysicsManager e um std::vector para armazenar as instâncias de SceneObject, como é feito no código abaixo:

class Tutorial1 
{
    private:
        Ogre::Root *mRoot;
        Ogre::Camera* mCamera;
        Ogre::SceneManager* mSceneMgr;
        Ogre::RenderWindow* mWindow;

        PhysicsManager mPhysics;

        std::vector<SceneObject *> mObjects;

        //resto do código existente
        //...
};

São declarações bem simples e similares com as que realizamos em outras partes do projeto, além dessas precisamos adicionar um código no destrutor da classe Tutorial1 para liberarmos a memória dos SceneObjects, isso é feito no código abaixo:

~Tutorial1(void)
{            
    for(int i = 0, len = mObjects.size();i < len; ++i)
        delete mObjects[i];

    delete mRoot;
}

O código acima apenas percorre o vetor e destrói qualquer objeto que tenha sido alocado, sem grandes complicações.

Agora que já temos uma infra-estrutura básica no programa exemplo, vamos adicionar um método para criar um objeto de cena e um corpo rígido. O método irá se chamar createBoxObject e vai criar apenas caixas através dos parâmetros:

  • name: nome do objeto a ser criado, que vai ser usado para nomear o SceneNode e a entidade do Ogre.
  • size: um Vector3 que irá conter as dimensões da caixa
  • pos: a posição inicial do objeto
  • mass: a massa do objeto
  • shape: a forma de colisão a ser usada com o objeto

A definição completa do código podemos ver abaixo:

void createBoxObject(const char *name, const Ogre::Vector3 &size, const Ogre::Vector3 &pos, float mass, btCollisionShape &shape)
{
    Ogre::SceneNode* node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode(name);
    
    btRigidBody &body = mPhysics.createBody(btTransform(btQuaternion::getIdentity(), btVector3(pos.x, pos.y, pos.z)), mass, shape);
    
    mObjects.push_back(new SceneObject(*node1, body));
    
    Ogre::Entity *entity = mSceneMgr->createEntity(name, "Prefab_Cube");
    node1->attachObject(entity);

    node1->setScale(size.x / 100.0f, size.y / 100.0f, size.z / 100.0f);
}

A primeira ação do método é criar um SceneNode que irá representar o objeto na tela, isso é feito usando o código do Ogre que já conhecemos nos tutoriais de Ogre do PontoV.

Logo em seguida criamos o corpo rígido usando a função createBody que definimos no inicio do artigo, repare que o corpo é criado com a rotação inicial sendo a identidade, ou seja, sem rotação e configuramos a posição deste usando Vector3 passado como parâmetro, observe também que precisamos converter o Vector3 do Ogre para o btVector3 da Bullet.

De posse do corpo rígido e do SceneNode, podemos então criar o SceneObject e adicioná-lo no vetor que usamos para controlar as instâncias ativas.

Continuando iremos criar então uma Entity para termos um modelo 3d representando o objeto. Poderíamos muito bem carregar um modelo externo, mas como vamos apenas representar cubos, utilizamos um objeto pré-fabricado do Ogre, que já vem com formas primitivas básicas definidas. Após criar o cubo, anexamos ele ao SceneNode que criamos.

Um ultimo passo na criação do nosso objeto é ajustar o tamanho do modelo 3d ou do cubo. O cubo pré-fabricado pelo Ogre vem com dimensões 100x100x100, então alteramos a escala do SceneNode para que o cubo adquira a mesma dimensão do valor passado como parâmetro, nesse caso, o parâmetro size.

Por fim, como já temos um método prático para criação de objetos, vamos criar alguns para popular nossa cena, faremos isso na listagem abaixo:

void createObjects()
{
    btCollisionShape &planeShape = mPhysics.createBoxShape(200, 1.0f, 200);
    btCollisionShape &cubeShape = mPhysics.createBoxShape(10, 10, 10);
    btCollisionShape &plane2Shape = mPhysics.createBoxShape(50, 1.0f, 50);

    createBoxObject("cube", Ogre::Vector3(10, 10, 10), Ogre::Vector3(0, 100, 0), 1, cubeShape);
    createBoxObject("cube2", Ogre::Vector3(10, 10, 10), Ogre::Vector3(0, 80, 0), 5, cubeShape);
    createBoxObject("cube3", Ogre::Vector3(10, 10, 10), Ogre::Vector3(0, 130, 0), 5, cubeShape);
    createBoxObject("cube4", Ogre::Vector3(10, 10, 10), Ogre::Vector3(30, 150, 0), 10, cubeShape);
    createBoxObject("plane2", Ogre::Vector3(50, 1, 50), Ogre::Vector3(20, 110, 0), 1, plane2Shape);

    createBoxObject("floor", Ogre::Vector3(200, 1, 200), Ogre::Vector3(0, -50, 0), 0, planeShape);        
}

O método acima cria inicialmente três formas de colisão, que são atribuídas as variáveis planeShape, cubeShape e plane2Shape. Criamos-las em separado para podermos compartilhar o maior número possível entre os objetos. A primeira forma chamada de planeShape vai ser usada para representar o chão da cena, a segunda forma, cubeShape é usada para representar caixas que vão cair e por fim, a terceira é usada para representar uma prancha que cai junto com as caixas.

Em seguida temos varias chamadas a createBoxObject, sendo a primeira:

createBoxObject("cube", Ogre::Vector3(10, 10, 10), Ogre::Vector3(0, 100, 0), 1, cubeShape);

Nessa chamada estamos criando um SceneObject chamado de “cube”, que vai ter tamanho 10x10x10, posicionado nas coordenadas 0, 100, 0, com massa 1 e vai ter como forma de colisão a cubeShape.

Depois criamos mais 3 cubos, repare que são mais pesados que esse primeiro, isso é para eles causarem um maior impacto no primeiro cubo quando caírem, apenas para um efeito mais interessante no exemplo final.

No final temos a linha abaixo:

createBoxObject("floor", Ogre::Vector3(200, 1, 200), Ogre::Vector3(0, -50, 0), 0, planeShape); 

Esta é praticamente idêntica as anteriores, mas repare aqui que a massa do objeto é definida como zero e não custa lembra que isso significa que temos um objeto estático, que nunca vai se mover.

Preenchendo a Cena e Atualizando a Simulação

Agora que já podemos criar uma pequena cena de demonstração vamos modificar o método runApplication para que este inicialize a cena e depois cuide de simular nossa física, primeiro vamos adicionar a chamada ao método createObjects, logo após a configuração da câmera e depois no loop principal cuidar de atualizar a simulação, o código todo pode ser visto abaixo:

bool runApplication()
{            
    // Posiciona a Camera
    mCamera->setPosition(Ogre::Vector3(0, 0, 300));
    // Manda a camera olhar para um ponto
    mCamera->lookAt(Ogre::Vector3(0, 0, -300));
    // Distancia mínima para que o objeto deve estar da camera para ser renderziado
    mCamera->setNearClipDistance(5);
    // Distancia máxima para que o objeto deve estar da camera para ser renderziado
    mCamera->setFarClipDistance(5000);            

    createObjects();

    // Cria Luz ambiente
    mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
     
    // Cria uma Luz
    Ogre::Light* l = mSceneMgr->createLight("Luz");
    l->setPosition(20, 80, 50);                

    //Loop Principal
    for(;;)
    {
        // Processa as mensagens que o Sistema Operaciona envia para aplicação
        Ogre::WindowEventUtilities::messagePump();

        // Parar o programa caso a janela seja fechada
        if(mWindow->isClosed()){
            return false;
        }

        mPhysics.update(0.001);

        for(int i = 0, len = mObjects.size();i < len; ++i)
            mObjects[i]->prepareToRender();


        // Renderiza Um Frame
        if(!mRoot->renderOneFrame()) 
            return false;
    }
    return false;
}

O código é bem similar ao código original, apenas adicionamos:

  • a chamada a createObjects logo após configuração da câmera para que fossem criados os objetos.
  • No loop principal invocamos o método update do PhysicsManager para que este rode a simulação física, repare que não usamos qualquer informação de clock, apenas colocamos um valor hardcoded para testarmos a simulação.
  • Após a chamada de update no PhysicsManager, percorremos a lista de SceneObject invocando o método prepareToRender de cada um, assim garantimos que cada SceneNode vai estar na posição correta.

    Repare também que foi removido o código original que carregava modelos 3d aleatoriamente, pois agora vamos utilizar apenas os objetos que criamos no código.



  • Comentários (15)
    • Luis Eduardo Nery Santos  - Visual Studio 2010
      avatar

      Olá, Bruno.

      Estou criando um jogo estilo Arkanoid, como projeto na faculdade.

      Gostaria de saber se essa biblioteca é indicada para utilizar em jogos 2d, feitos com c++ e OpenGL e também estou com duvidas na hora de configurar a variavel de ambiente no visual studio 2010, pois o VC++ não está habilitado.

      Parabéns pelos posts.

      Obrigado. =)

    • Vinícius Godoy de Mendonça
      avatar

      Para jogos 2D provavelmente será mais fácil utilizar a Box2D:
      http://box2d.org/

      O VC++ só será habilitado depois que você criar o primeiro arquivo .cpp dentro do seu projeto. Crie ele e o menu aparece. :)

    • Luis Eduardo Nery Santos  - Instalação da biblioteca
      avatar

      Valeu Vinicius! vou utilizar essa biblioteca para fazer meu jogo então.

      Mas eu não sei muito bem como instalar bibliotecas, tem algum tutorial pra me indicar?

      ou se for simples tem como dar uma explicada resumida?

      Obrigado. :lol:

    • Bruno Crivelari Sanches
      avatar

      Luis,

      no geral bibliotecas C/C++ seguem sempre um mesmo padrão:
      - extrair os arquivos para algum lugar
      - configurar diretório de includes do compilador
      - configurar diretório de libs do linker
      - nas configurações do seu projeto, adicionar as bibliotecas novas como dependencias

      Nesse artigo é feito tudo isso e ainda inclui o passo extra de compilar a bilbioteca. O processo pode variar uma biblioteca para outra, mas os passos básicos são os que listei aqui.

      Esse outro artigo mostra o processo para uma outra biblioteca (SDL) e explica em mais detalhes alguns conceitos, serve como uma "receita" para instalação de bibliotecas:
      http://pontov.com.br/site/cpp/46-conceitos-basicos/1 55-como-usar-bibliotecas-cc

    • augustowebd  - Versão para impressão
      avatar

      Bruno mais uma vez parabéns pelo excelente artigo, só gostaria de solicitar, não querendo ser abusado, uma versão para impressão completo, porque a versão para impressão atual pega apenas a página corrente.

      Grato.

    • Bruno Crivelari Sanches
      avatar

      Obrigado Augusto!

      Quando clico ali no "Todas as páginas" e depois na impressorinha, funciona com todas as páginas.

      T+

    • Napoleon  - Oiii
      avatar

      :cheer: Ei eu gostei muito de sua matéria, tem como falar do box2D :woohoo:

    • Bruno Crivelari Sanches
      avatar

      Oi Napoleon, vou por na lista aqui! :)

    • japaseki
      avatar

      Fala Sanches, estou tentando executar este projeto mas quando ele entra na tela para escolher o render (opengl ou DIRECTX), ele cai automaticamente....o q poderia ser ?

    • Bruno Crivelari Sanches
      avatar

      Olá,

      você seguiu a risca as instruções da ultima página no ultimo item "Configurando o Projeto" ?

      Se sim, tem que depurar para ver onde ocorre o erro e o porque.

      Para saber como usar o depurador: http://www.pontov.com.br/site/cpp/41-visual-c/216-como-usar-o-depurado r-do-visual-c

      T+

    • matheus bueno
      avatar

      ola,


      parabens pelo post, muito bom mesmo,

      fugindo um, pouco do assunto, eu estou criando um jogo( na verdadde é uma especie d PacMan modficado) em allegro, tem como usar a box 2D junto com o allegro, e se tiver, compensa usar?

      obriigado.

    • Bruno Crivelari Sanches
      avatar

      Para um pacman? Não vejo porque usar biblioteca de física. É um sistema de colisão tão simples.

    • Matheus Bueno
      avatar

      no caso haveriam modificações nele, seria meio temático, por exemplo em uma faze com o tema espaço sideral os fantasmas seriam et's que disparariam lazer's e por ai vai, o pacman também ganharia up grades de acordo com os pontos acumulados, esses up grades poderiam ser, por exemplo, mais tempo invencivel quando se come uma pirula, ou almento na velocidade,pensei q em algumas ocasioens aplicar uma biblioteca de física facilitaria meu trabalho.

      o problema maior no jogo esta sendo a AI, mas eu ta pensando em criar um grafo pra representar o labirinto, e calcular o menor caminho entre o fantasma e o pacman, só q eu to em duvida, se isso vai pesar meu jogo(se existe uma solução melhor ficaria grato em ouvir).

      quanto a biblioteca de fisica, gostaria de saber apenas se existe essa possibilidade, ate pra outros jogos com allegro, o pacman é meu primeiro jogo, é mais pra aprendizado, mas eu gostaria de fazer direitinho.

    • Bruno Crivelari Sanches
      avatar

      Pesar? Depende de onde você vai rodar, se for para um PC, um pacman dificilmente vai pesar.

      Sobre a física não vejo dificuldades em integrar com Allegro, geralmente as bibliotecas são independentes de API, como foi mostrado no artigo.

      T+

    • Aml  - Octree e outros
      avatar

      Bruno, antes de mais nada, Parabéns pelos artigos.
      Eu vou utilizar a engine, mas fiquei com uma dúvida:
      Bullet já trabalha com Octree ou é necessário implementar em caso de múltiplos elementos no cenário?


    Escrever um comentário
    Your Contact Details:
    Gravatar enabled
    Comentário:
    [b] [i] [u] [url] [quote] [code] [img]   
    :angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
    :X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
    Security
    Por favor coloque o código anti-spam que você lê na imagem.
    LAST_UPDATED2  

    Busca

    Linguagens

    Twitter