*Aviso ao navegante, artigo técnico ao longo 🙂 Prossiga por sua conta e risco *

Eu tenho trabalhado direto com a GameEngine do Blender. Uma das coisas que tenho reparado, é a quantidade de voltas e truques que temos que fazer às vezes para resolver operações bem simples.

Bom, esta semana estava precisando otimizar o processo de deleção de objetos na GameEngine. Normalmente, para se deletar um objeto, é necessário alguns passos.
Por exemplo:

1) Se cria uma propriedade (property) booleana chamada DELETAR que tenha como valor False.

2) Se cria um sensor do tipo Property, que seja ativado toda vez que a propriedade DELETAR seja True.

3) É necessário usar um actuator EditObject->EndObject associado a este sensor para então deletar o objeto assim que se mudar a propriedade DELETAR para True.

4) E por fim, é preciso um código em Python ou algum outro método dentro da GameEngine para alterar a propriedade DELETAR para True do objeto que queremos eliminar.

Como estava achando este método muito pouco prático (por mais que seja bem engenhoso), resolvi ir atrás de outras soluções.

Acabou que resolvi que o melhor seria alterar o código do Blender. Nessas horas que se vê como é bom trabalhar com software livre.

Esta não é a primeira vez que mergulho no código do Blender, já tinha outras vezes alterado o limite de Clipping do 3Dview, e mais recentemente venho tentado habilitar um método de estereoscopia espelhada na GameEngine.
Bom, por isso mesmo eu sabia que seria mais uma aventura tentar botar a mão na massa. Respirei fundo, e comecei a futucar.

Para mexer no código do Blender, a primeira coisa que temos que fazer é o download do código-fonte. É mais fácil trabalhar no código oriundo da versão oficial, acessível para download no site da Blender Fundation. Uma outra opção é fazer o download do código da versão de desenvolvimento do Blender, o mesmo código que os desenvolvedores modificam diariamente (este código na verdade é bem grande, e não se recomenda baixá-lo a não ser que você precise mesmo).

Além disso tem que baixar uma séria de bibliotecas para se compilar o Blender, e alguns programas também. Não vou entrar no mérito de como se fazer isso, mas basicamente você precisa de um compilador (MSVC, MinGW, gcc, …), de um programa de configuração (CMake, SCons,…) de bibliotecas OpenGL instaladas, e das bibliotecas disponíveis no arquivo SVN do blender em /blender/lib/… Para quem realmente for encarar o desafio, no final deste artigo tem uns links para alguns sites que explicam alguns passo-a-passo para se compilar o Blender. (No Linux é bem mais simples, diga-se de passagem).

Com tudo configurado e funcionando, eu recomendo se tentar substituir a Splash screen (a tela inicial) da versão do Blender que voce estará modificando, assim você vai se familiarizando com o Blender, e começa a se divertir.

Screenshoot Splash Screen

Como não sou nenhum expert em C++ (a linguagem de programação do Blender) o jeito que eu arranjo pra me virar é tentar copiar parte do código existente que eu saiba que tem alguma relação com o que estou querendo mudar, e tentar adaptar ao meu objetivo.

Primeiro eu procurei no código que o actuator EndObject faz. Com a ajuda do MSVC, localizei o arquivo KX_SCA_EndObjectActuator.cpp, onde consta o seguinte código:

bool KX_SCA_EndObjectActuator::Update()
{
// bool result = false; /*unused*/
bool bNegativeEvent = IsNegativeEvent();
RemoveAllEvents();if (bNegativeEvent)
return false; // do nothing on negative events
m_scene->DelayedRemoveObject(GetParent());return false;
}

Gotcha! Então agora tudo o que eu precisava era criar uma função associada aos objetos do Blender e fazer esta função rodar estes comandos.

Para descobrir quais sao as funções associadas a um objeto na GameEngine do Blender (BGE), podemos usar o comando dir.

Por exemplo, para a cena inicial do Blender, se quisermos saber quais funções podemos chamar a partir do cubo existente, precisamos de um script mais ou menos assim:

import GameLogic
objeto = GameLogic.getCurrentScene().getObjectList()[“OBCube”])
print dir(objeto)

Se rodarmos este script, o resultado será algo como:

[getPosition], [setPosition], […]…

Bom, o que estou querendo é justamente criar mais uma função destas, então de volta ao código do Blender vamos procurar por getPosition.

Fui encontrar em dois arquivos as referências para estas funções, são eles: KX_GameObject.cpp e KX_GameObject.h.

O header(.h) é uma espécie de arquivo que contém todas as declarações e funções que são usadas pelo módulo do programa.

Lá eu encontrei a linha:

KX_PYMETHOD_NOARGS(KX_GameObject,GetPosition);
KX_PYMETHOD_O(KX_GameObject,SetPosition);

E no arquivo com o código (.cpp) encontrei:

PyMethodDef KX_GameObject::Methods[] = {
{“getPosition”, (PyCFunction) KX_GameObject::sPyGetPosition, METH_NOARGS},
{“setPosition”, (PyCFunction) KX_GameObject::sPySetPosition, METH_O},

Oba, é por aí mesmo 🙂
No header (KX_GameObject.h) eu escrevi:

KX_PYMETHOD_NOARGS(KX_GameObject,EndObject);

E no código (KX_GameObject.cpp) eu escrevi:

{“endObject”,(PyCFunction) KX_GameObject::sPyGetPosition, METH_NOARGS}

O Blender compilou sem problemas, e quando rodei o script acima, eis o resultado no console:

dir(objeto):
[getPosition],[setPosition],[endObject]

O passo seguinte foi tentar criar a minha função endObject (até então eu estou na verdade chamando a função getPosition quando eu uso o endObject()). Para isso no arquivo KX_GameObject.cpp eu procurei novamente pela chamda getPosition, e encontrei o seguinte código:

PyObject* KX_GameObject::PyGetPosition(PyObject* self)
{
return PyObjectFrom(NodeGetWorldPosition());
}

Copiei este código em baixo, mudei o nome da função, e resolvi começar a escrever minha função.
Mas é claro que não ia ser tão fácil assim.

No arquivo que vimos acima, KX_SCA_EndObjectActuator.cpp,  primeiro se chama a cena, e depois se deleta o objeto pai(Parent) do nosso objeto. No caso da nossa função, não dá pra deletar o objeto pai, mas se usarmos scene->DelayedRemoveObject(this), conseguimos remover o objeto. Mas aí surge um problema. Se deletarmos o objeto, qual objeto devemos retornar no final desta função? (Repara que no código o que estou chamando de função é na verdade um Objeto Python, portanto devemos terminar a função retornando algum objeto para o código).

Depois deste Koan Zen, resolvi calçar as sandálias da humildade e tentar estudar mais um pouco a documentação disponivel no site do Blender. Depois de muito fuçar em páginas desatualizadas e complicadas demais pra mim, descobri que existe um canal no IRC só para desenvolvedores da GameEngine trocarem informação – #gameblender.

Instalei o ChatZilla no meu Firefox e entrei lá. Perguntei educadamente e em inglês se havia alguma desenvolvedor online, porque eu estava precisando de uma assistência.

No final tive uma grande ajuda do usuário scabootssca, que me explicou dentre outras coisas, que a solução era retornar um objeto chamado Py_None. Então vamos lá, return Py_None;

Compila, roda o arquivo de teste, e …..

Yahoooooooooooo!!!!!

Está funcionando perfeitamente.
Acabei aprendendo também como fazer um patch (um arquivo contendo somente as modificações que voce tenha feito do código fonte) e enviei para o site de contribuições do Blender.

(para acessar este link é necessário ter uma conta no site)
https://projects.blender.org/tracker/index.php?func=detail&aid=15865&group_id=9&atid=127

Estou bastante contente. Não só pela solução em si, mas pela sinergia que existe no usar e desenvolver o Blender. É muito rica a experiência de interagir diretamente com quem desenvolve o programa, e ser capaz de contribuir também.

E não é que dia seguinte pela manhã recebi a confirmação no meu email de que o patch tinha sido aceito 🙂

Agora só me resta esperar a próxima versão oficial do Blender. Eu fico muito feliz que tenham incorporado esta função no programa. Além de ser uma mão na roda para o projeto em que estou envolvido, isso me enche de orgulho 🙂

Um grande abraço,
Dalai

Alguns Links:

O Patch EndObject como ficou 🙂

O artigo sobre meu segundo patch (na verdade uma correção de um bug)

E alguns tutoriais em inglês:

5 Thoughts on “O primeiro patch a gente nunca esquece

  1. Parabéns Dalai,

    Essa iniciativa de fazer você mesmo é fundamental para projetos como o Blender.

    Agora, qual é a próxima ferramenta ou função que você pretende desenvolver??? 😉

    Abraços

  2. Obrigado Allan 🙂

    Eu concordo contigo. É justamente nesta dinâmica de conversar com desenvolvedores, fuçar o código, reportar bugs de um jeito cuidadoso, produzir tutorial, … que está o diferencial do software livre.

    É por isso que acredito que sites como o seu são tão importantes 😉

    E não é que meu segundo patch saiu hoje, hehehe.

    Estávamos com um problema com o Actuator TrackTo, e fomos atrás da solução por conta própria.

    Depois dá uma conferida lá:
    http://blenderecia.orgfree.com/index.php/blender/um-e-bom-dois-e-demais/

    Abração,
    Dalai

  3. Sen-Sa-Cio-Nal!!!
    Primeira vez que alguém explica tão claramente por onde começar a mergulhar lá dentro…

    Obrigadão!

  4. Grande Yorik!

    Fico contente que tenha gostado, e espero ver uns patches by Yorik 🙂

    Pra você que já programa com certeza será mais fácil. E eu garanto que o trabalho de detetive vale a pena, hoje por exemplo enviei mais um patch pro Blender.

    Foi um patch bem pequeno para ativar wireframe mode no vertex paint mode. Mas pra mim o interessante agora é que consigo ver isto com mais naturalidade, e mesmo para reportar bugs fica mais fácil explicar o que está acontecendo vendo o código e tentando resolver por conta própria.

    Como tenho tido uma boa resposta, e como mesmo em inglês está difícil encontrar material relativo a esses assuntos, eu estou preparando um terceiro artigo desta “série” pra falar sobre como reportar bugs e pequenos patches pro Blender.

    Bom, no que puder vou continuar contribuindo 🙂
    E obrigado pelo feedback.

    Um grande abraço,
    Dalai

  5. Cara, que demais. Mas eu não preciso disso para me divertir no blender.

    Ele é muito irado incluindo a game engine imbutida nele!!!

    Abraços!!!

Leave a Reply

Your email address will not be published. Required fields are marked *

Post Navigation