Este blog esta em reforma no momento.

Animando o Corpo-Seco


Gente eu apanhei feio, mas comecei a descobrir onde estão os controles do Blender 2.5 para animação.
Muitos dos controles não mudaram, simplesmente ficaram mais aprimorados, pelo menos é oque aparente dando uma olhada inicial no DVD de Sintel.
O difícil é agente se readaptar aos atalhos.

Offtopic
O DVD é realmente luxuoso, tem tutoriais, o filme, documentário, extras e todos os elementos de produção (sim eu comprei na pré-venda ano passado antes de começar a produção).
Se quando eu comecei a usar o Blender tivesse essa quantia de material para aprender eu teria sido muito feliz.

Jacaré e o aprendizado da nova interface de animação

Jacarés ficam esperando suas presas no charco, nos mangues e nas montanhas geladas e nos esgotos das cidades.
Predadores vorazes sua poderosa mandíbula é capaz de entortar o aço mas seus dentes não cortam. Ele esmaga sua presa e em seguida tenta arrancar pedaços dela com violentos movimentos.
As raças conhecidas são os jacarés dos mangues, jacarés do esgoto, jacarés mandíbula-gigante (que vivem nas montanhas) e jacarés alados.
Os especialistas dizem que os jacarés evoluíram de largatixas do período permiano. E que uma maneira de se imobilizar o jacaré é pô-lo de barriga para cima e fazer-lhe cafuné na barriga.

 E modelagem foi extreme-low-poly, talvez meu senso numérico tenha falhado um pouco mas no final das contas acho que atingi um bom custo benefício.

Pintar foi gostosinho os pincéis do Blender agora aparecem com miniaturas para seleção.
Animar que esta sendo complicado, ainda não entendi direito o novo mecanismo, não acho onde estão os controles de frames individuais para cada bone e o menu de propriedades do lado é muito mais cheio de coisas (também esta condensando vários painéis do Blender antigo).
Mas logo animo ele direito.

Corpo Seco NPC

Um "Corpo-Seco" é tradicionalmente um morto-vivo que se confunde com a vegetação pois o aspecto da sua carcaça lembra galhos de árvores secos e distorcidos.
Só se transformam em corpos-secos pessoas que foram extremamente más em vida, essa ideia é bem antiga deve vir até da Europa pré cristã e chegou à nossas roças.
A saber um Litch é um mago que viveu pela maldade e fez grandes crueldades em nome do mal para re-viver como um dos demônios mais fortes. Já um corpo-seco é uma pessoa que fez uma maldade tão grande, como por exemplo um funkeiro que vai num asilo e fica cantando creu no ouvido dos velhinhos, uma mulher que põe laxante em saco de bala de São Cosme e Damião, etc. Essas pessoas por sua maldade tão grotesca são renegados até no inferno e a própria terra enojada os cospe.
São almas condenadas com sede de sangue dos vivos.
No jogo eles estarão escondidos no mato camuflados.
Eu gostei do resultado da modelagem, fiz ela em um dia, e depois o normalmap.
Ainda irei pintar e se possível acrescentar detalhes de folhas e galhos de árvores na pintura e no normalmap.

Mas que ele ficou bonitinho ele ficou, o resultado ficou mais a cara de SD do jogo do que os meus primeiros mortos-vivos.

Coelhinhos from hell

Cibele encontra uma multidão indo em direção oposta, e imediatamente os adverte:
-Não, pera! Onde vocês vão? Não podem ir por esse caminho.
-O que? Por que não?
-Tá cheio de "coelhinhos" lá! Eles são medonhos e surgem do nada.
-Ora menina, não nos atrapalhe. Essas pessoas perderam suas casas essa semana, e ontem mesmo tivemos que passar por um pântano cheio de crocodilos.
-Crocodilos não são NADA! Aqueles coelhinhos são o diabo, eles vão matar vocês!
-Desapareça!
Dando um safanão que a jogou longe o homem saiu conduzindo a multidão de pessoas tristes. Enquanto passava Cibele ouve deles os lamentos: "-Eles vieram de noite!", "-Minha esposa, meus filhos...", "-Malditos clérigos de Nagdahbastarthen!", "-Minha plantação, todo meu trabalho", "-Meu ouro, minhas ferramentas".

Coelhinhos, entidades abissais que surgiram na terra após fugir do inferno. Criaturas com uma sede de sangue insaciável e selvageria impressionante, atacando mesmo o oponente depois de morto.
Caracterizam-se pela emboscada onde a vitima distraída é morta quase que instantaneamente pelas suas pressas gigantes. Ou se sobreviver ao ataque morre em poucas horas devido ao poder infeccioso das bactérias mutantes que vivem na gengiva do coelhinho.


Uma boa saída entre fases, para situar o jogador dentro da trama. E uma desculpa para pôr coelhinhos na cena.
Notem que pela descrição os coelhinhos além de dano de perfuração tem dano de veneno e técnicas como "fingir de morto" não funcionam.

101 postagens

Puxa eu nem tinha reparado até ver o número redondo 100 no painel de controle do Blogger

http://wildwitchproject.tumblr.com/
Bom, uma ferramenta nova que estou usando é o Tumblr, um sistema de blogs que se propõe a ser simples até o talo, tanto que mais lembra um Twitter doque um site de blog, Só que com as vantagens de ser encorpado.
Comecei as primeiras customizações no serviço, que me permitiram fazer muitas coisas, como faço atualmente no Blogger.
Um mínimo de conhecimento de CSS já da pra fazer estrago. Acho até que vou fazer uns tutoriaizinhos pra chamar a atenção pro blog :P

Bom, em outras palavras isso representa uma ferramenta extra e não uma futura substituição.
O Blogger continua sendo a unidade principal que gera textos e documentos para o jogo, o Tumblr entra substituindo o twitipic, onde eu posso postar imagens e ideias rápidas sem compromisso.

Modelos para Dezembro Hárpia, Rapy and Zody e Mecha

Bom, vamos aos progressos coisas soltas mais recentes.
Vocês já estavam pensando que ele não ia sair do concept, né? Este é o inicio da modelagem do Dr Rapy and Zody.
Pra quem não se lembra ele é um cientista louco que ficou décadas preso num sanatório e quando escapou durante uma guerra civil montou um monstro com pedaços de corpos que encontrou no campo de batalha. Vejam os detalhes  aqui.
A modelagem da hárpia também começou. As hárpias são seres metade pássaro e metade sapatão que sequestraram a "linda" filha do Rei Trololow e a prenderam numa gaiola para que só elas desfrutassem da sua linda e melodiosa voz.  Ela é outro modelo que estou refazendo e esta bem melhor olhem só.
Este modelo esta meio que na modelagem ~ concept, mas é um mecha para uma das fases.
Bom, não sou muito bom no design de mechas, mas acho que dá pra quebrar o galho.
Algumas soluções surgem no momento da necessidade, como nesse sistema de conexão das asas. Acabou que deu uma boa visão esteticamente.
Abro o MyPaint em segundo plano, a cabeça começa a flutuar em tantas coisas que aproveito para rascunhar algumas imagens. Olha a Loira do Banheiro ai, o problema desses personagens bonitos é que agente se apaixona por eles e fica o tempo todo repensando.

Homem Poraque

Thessalius Ludicus Victor
Convencido, inescrupuloso e bastante forte.
A palavra cuzão é a que seus "amigos" melhor usavam para descreve-lo.
Sempre arrumou confusão na cidade onde vivia até que um dia sua ambição o fez ser banido para uma ilha deserta no meio do "grande rio".
E porque ele não saiu de lá nadando? Simplesmente porque a ilha fica circundada por fendas como se tivesse cachoeiras por todos os lados. Nenhum navio consegue entrar ou sair de lá.

Tessalius é um poraque, bem mais abrasileirado doque um tritão com tridente de raios, mas igualmente mortal.

A ideia é que Cibele encontre ele com algo em mãos que o ajudará a escapar da prisão se possível em uma quest alternativa.

Bom, não estava muito inspirado hoje. Quem sabe mais adiante eu melhore sua história.

Pensando no sistema de update

Nada muito grande mas como se trata de um jogo que pode levar muito tempo até chegar no final uma opção é realmente os updates.
Bom, como abrir um jogo e receber um aviso: "Favor atualizar seu cliente" é uma coisa chata até no cliente do World of Warcraft.

Então vou tentar incorporar a carga durante a execução do jogo. Teoricamente é simples e usando a experiência adquirida com sockets e threading desses últimos messes será "pá-pimba".
Isso também representa problemas como o desses desenhos: do jogador entrar num cenário, sair, fazer uma quest e voltar e encontrar um cenário modificado.

No entanto usando a arquitetura de database fica mais prático fazer isso do que com estruturas de arquivos, além de poder ter um controle de versionamento do jogo nas mãos do jogador!
É um rascunho de ideia meio doida mas coerente.

Estou adicionando essa ideia no documento de de coisas boas e ruins sobre jogos. Lembram-se dele? É uma tabela colaborativa que eu disponibilizei esses tempos aqui.

Como adotei a ideia de banco de dados como estrutura de arquivos, essa estratégia deve ser fácil de ser adotada, nem que represente clonar uma tabela que esta sendo usada em tempo de execução para realizar o update de forma suave.
(Sim tenho um certo vicio em databases, afinal são quase 15 anos de programação web  XP )

Esses tempos eu me estressei com o recurso das "migrates" do framework Rails, e agora estou eu aqui pensando em montar scripts Lua para fazer o mesmo!!!

Teoricamente é só mandar código que automatize a migração de maquinas de estado dentro e fora do jogo. Bom o tempo dirá se essa teoria é de fato tão simples quanto parece.
^^

Distraindo a cabeça com um novo header

Não vai ajudar nada no desenvolvimento, mas vai fazer um bem a mim e bem pro blog mudar um pouco a cara.
Desenhar e projetar um novo header me faz relaxar.
Meu traço e habilidades de desenho taum um lixo. Só dão pro gasto.
To sem ideias de pose, mas a clássica serve também.
Para deixar o desenho um pouco mais bombado resolvi esculpir o relevo da personagem e jogar como um bumpmap para iluminação.
Agora o novo header vai ter uma interação com a fonte de luz (também).
Esse teste de implementação ainda esta pouco animador. Usei um exemplo que achei neste site, mas o algoritmo serve apenas para "luz direcional".
A chave do bump é essa matriz muito simples, até impressiona (se eu soubesse matemaquiques nesse nível estaria feliz!)
Bom, terminando o freela chato, e acertando o ritmo de postagem no RPG Vale eu volto com força pro projeto.

Recebi uns feed-back positivos do projeto essa semana que me deixaram muito feliz e deram um animo ^^

terra brasilis et difficile

Meu ritimo de postagem diminuiu um pouco, por sorte meus acessos não muito.
 

Bom, olhando a imagem acima vocês podem ver que as plantas parasitárias ficaram um lixo.
Então vou deixar a modelagem manual de lado um pouco. Assim que estiver plotando esta arvore no ambiente do game, vou tentar implementar um sistema de particulas para fazer o trabalho.

A ideia de fazer arvores próximas à realidade que eu encontro ainda esta meio confusa e complicada.
Este modelo de arvore ficou com cerca de 1000 triângulos. Algo bem realista, e fácil de aplicar um LOD.

Pode parecer muito polígono, mas essa é uma das coisas que eu sabia que iam aparecer desde antes de começar o projeto oficialmente.

A implementação com a OgreKit ainda esta nos testes, mas pelo visto vai ser ela mesmo. Talvez eu tenho que migrar do Eclipse para o CMake (texto puro), algo que não deve ser tão ruim assim. No final das contas o Eclipse só esta servindo pra corrigir erros de digitação.
Não me acerto em fazer o Code Blocks funcionar e o Visual Studio não mostro nenhuma vantagem sobre o Eclipse.
Estou preparando o projeto de objetos interativos. Essa imagem é um aviso que o jogador terá que fazer algum teste para conseguir decifrar.

Outro passo corrido é o "model sheet" da Cibele. Eu tenho alguns outros estudos dela em alguma pilha de papel mofado mas não no formato SD que irei usar no jogo.

Bom, uma semana e meia de freela dor-de-cabeça atrapalhando tudo, mas os problemas foram pro ralo.

Arvores da floresta de Aracne

Criando árvores gigantes para a cena da Aracne.
A produção da malha textura da árvore esta em andamento, o primeiro passo foi pegar uma textura real de árvore...
Lugar da foto no Google Maps
e transformar em textura ladrilhável com a ferramenta clone e o filtro de "Tornar encaixável".

Dei também um acabamento de desenho à árvore. O processo inclui saturação, posterização e sobreposição de camadas.

O segundo passo foi criar um plano, multiplica-lo com Alt+D (clone). Setar a UV para pegar todo o quadrado e depois sub-dividilo em centenas de vezes. Agora o relevo é feito com o Sculpt.

Já as pequenas trepadeiras foram acertadas no GIMP com auxílio do normalmap plugin.

A imagem gerada no GIMP foi usada no material dentro do Blender, na hora do bake ele é incorporado ao resultado final. Assim temos um mapeamento muito rico com poucos poligonos.

As folhas e galhos menores serão feitos com textura. Do mesmo modo que no Yo Frankie.
Minha árvore esta virando um Frankenstein, mas usar imagens de perto é tradição des-de as primeiras tentativas.

O tipo de mata
Bom, a ideia é criar um ambiente denso, sombrio e de árvores altas. Logicamente tenho dificuldade em achar um lugar como esse como referência.
Ao olhar árvores altas vê-se que os galhos ficam em cima mesmo. Tomei a liberdade de experimentar os modelos com galhos saindo do meio, assim posso utiliza-los para deixar o cenário mais complexo. Podendo Cibele caminhar sobre as árvores.
A árvore é composta de três malhas, a principal, os galhos intermediários e as folhas. Em proporções reais ela tem 40 metros.

Uma mata brasileira seria um problema mesmo para GPUs modernas.
Tentarei posteriormente ver se consigo usar um sistema de carregamento híbrido da gamekit com o carregamento de arquivo mesh normal da OGRE para usufruir dos recursos de LOD da OGRE.

A divisão do tronco da árvore em 2 malhas é para facilitar a física, os galhos menores não devem ser acessíveis (espero).

Páu Mulato - Deve ter uns 25~30 m.

Escrevendo roteiro e pensando em I.A.


Eu aqui pensando reversamente: o personagem, a ação e oque precisaria em forma de código.
Lembra um pouco aquela dos “ditos populares em php”. Mas em fim.

Sou Aracne, não lembro qual nome usei no passado antes de ser esta forma que me conheço agora. Não importa mais.

class CAracne (Char)
CAracne.passado = nil
CAracne.nome = “Aracne”
CAracne.idade = “?”

Aracne = CAracne.new()

Escuro, húmido e gostoso. O sol lá fora não me incomoda, estou feliz na sombra. Ouço os pássaros e os insetos da floresta. Apenas a harmonia escura a sombra. O cheiro do musgo, da casca da árvore e da flor, o cheiro do que me serviu de alimento.... a harmonia me afasta a solidão.

Aracne:attachSensor( Vista:new() )
Aracne:attachSensor( Audio:new() )
Aracne:attachSensor( Olfato:new() )
-- regula parãmetros para o personagem
Aracne.olfato.dist = 5
Aracne.audio.dist = 30 -- 30 metros

-- procura sombra, as áreas de sombra já estão demarcadas
-- no terreno. Escrita como função genérica para ser reaproveitada
-- por outros personagens.
function Char:procuraSombra( )
    local pos = self.phys.position
    local initialDist = 50
    sombra = nil
    for el in mapa:getElementByGroup(“sombra”)
        if el:dist( self ) < initialDist then
            initialDist = el:dist(self)
            sombra = el
        end
    end
    return sombra
end

Mas... que som é este? Se esgueira, é pesado, não é rato, nem coelho. Alguém talves? Comida para mais meses ou um sonho que me despertou?
É cheiro de homem... não tem algo a mais nele me incomodando. É cheiro de fêmea. Humpf, apenas comida.
Está chegando perto,  mais perto, quase perto, perto, AGORA!

function CAracne:main()
    if self.repouso then
        if self.audio:proximidade() or self.visao:proximidade() or self.olfato:proximidade() then
            self.epouso = false
        end
    else
        if self.primeiroAtaque then
            self.animation.set(“camuflagem”)
            local dist, char = minimo( {self.audio.alvo(), self.visao.alvo(), self.olfato.alvo() } )
            if dist <= self.distanciaDeBote then self.bote( char ) end
        else
            self.cacar()
    end
    self.continueAcao() --executa a ação escolhida ou continua a ação corrente que pode ser uma acao de parado, ou movimento
end

Estudando o Gamekit OGRE

http://code.google.com/p/gamekit/

Este projeto me chama atenção, pois já implementa OGRE e Lua numa tacada só.
 
Possui um grupo completo de 7 integrantes em seu trabalho e uma atividade realmente alta no desenvolvimento (até o fechamento deste post eles atualizavam o Google Code todos os dias).

Não tem muita documentação no site, mas ao baixar o source ele vem junto com toda a API de Lua.

Lua
Bastante completa, e com um modelo de programação lindíssimo.
Notei até que possui um objeto de maquina de estado já pronto ^_^.

Nos exemplos que vem no doc a coisa parece bem interessante.

Demo = BaseClass(OgreKit.Engine)
function Demo:constructor()
   self.scene = self:getActiveScene()
end
function Demo:OnUpdate(delta)
         OgreKit.DebugPrint("Demo main loop running ==> " .. self.scene:getName())
end
demo = Demo()
demo:connect(OgreKit.EVT_TICK, demo, Demo.OnUpdate)

C++
Na pasta Engine tem muitos arquivos que indicam as potencialidades da engine.
gkEngine.h mostra que ele usa uma instância de OGRESingleton.
 
E o gkSceneManager parece receber um nome como parâmetro. Apesar de eu ainda não achar na API Lua onde se pode influenciar isso, já é algo promissor.


Dá pra alterar facilmente conforme minhas necessidades?
Olhando com atenção o mecanismo de carga de arquivos, vê-se que ele é bem estruturado em várias classes.
A função loadFile na verdade é apenas um manipulador caso ocorra algum erro.
gkBlendFile *gkBlendLoader::loadFile(const gkString &fname, int options, const gkString &inResourceGroup)
{
       bool resetLoad = false;
       try {

               return loadAndCatch(fname, options, inResourceGroup);
       }

Depois o arquivo é pedido emloadAndCatch:
gkBlendFile *gkBlendLoader::loadAndCatch(const gkString &fname, int options, const gkString &inResourceGroup)
{
       m_activeFile = getFileByName(fname);
       if (m_activeFile != 0)
               return m_activeFile;


       m_activeFile = new gkBlendFile(fname, inResourceGroup);


Ela tem um mecanismo de segurança para evitar que o arquivo em execução atualmente seja recarregado. Não vejo um motivo muito sustentável para essa segurança, mas trata-se de uma engine predestinada a usuário intermediário talvez por isso.

Essa classe de carregamento é na verdade um manipulador de recursos, já que nada mais é que um gerente para uma lista de arquivos.

Indo reversamente ao código acho:
bool gkBlendFile::parse(int opts)
{

       utMemoryStream fs;
       fs.open(m_name.c_str(), utStream::SM_READ);
Esse objeto utMemoryStream tem um nome sinistro pra quem procura um stream de arquivo...
Mas o prefixo indica que esta na pasta Utils então espero que seja mesmo útil XD
class utMemoryStream : public utStream
{
public:
       utMemoryStream();
       ~utMemoryStream();

       void clear(void);

       void open(const char *path, utStream::StreamMode mode);
       void open(const utFileStream &fs, utStream::StreamMode mode);
       void open(const void *buffer, UTsize size, utStream::StreamMode mode);

       bool    isOpen(void)    const   {return m_buffer != 0;}
       bool    eof(void)       const   {return !m_buffer || m_pos >= m_size;}
       UTsize  position(void)  const   {return m_pos;}
       UTsize  size(void)      const   {return m_size;}

       UTsize read(void *dest, UTsize nr) const;
       UTsize write(const void *src, UTsize nr);


       void    seek(const UTsize pos, int dir) const;

       void            *ptr(void)          {return m_buffer;}
       const void      *ptr(void) const    {return m_buffer;}

protected:

       void reserve(UTsize nr);

       char            *m_buffer;
       mutable UTsize  m_pos;
       UTsize          m_size, m_capacity;
       int             m_mode;
};
Notem o método sobrecarregado open, a criança parece "bonbadinha". Mas aqui já parece que estou encontrando as referências a stream de arquivo clássicas, o lance é subistituilas por um manipulador de buffer (que a classe já tem).
void utFileStream::open(const char *p, utStream::StreamMode mode)
{
       if (m_handle != 0 && m_file != p)
               utFileWrapper::close(m_handle);


       m_file = p;
       m_handle= utFileWrapper::open(m_file.c_str(), mode);
       if (m_handle)
       {
               if (!(mode & SM_WRITE))
                       m_size= utFileWrapper::size(m_handle);
       }
}

Vamos ao utFileWrapper, uma função grandinha:
utFileHandle utFileWrapper::open(const char *filename, int mode)
{
#if UT_PLATFORM == UT_PLATFORM_WIN32 && defined(UT_WIN32_FILE)

       DWORD dwDesiredAccess= 0;
       if (mode & utStream::SM_READ)
               dwDesiredAccess |= GENERIC_READ;
       if (mode & utStream::SM_WRITE)
               dwDesiredAccess |= GENERIC_WRITE;

       DWORD dwShareMode= 0;
       if (mode & utStream::SM_READ)
               dwShareMode |= FILE_SHARE_READ;
       if (mode & utStream::SM_WRITE)
               dwShareMode |= FILE_SHARE_WRITE;


       DWORD dwCreationDisposition= OPEN_EXISTING;
       if (mode == utStream::SM_WRITE)
               dwCreationDisposition= CREATE_ALWAYS;

       HANDLE h= ::CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, 0,
                              dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);

       return h == INVALID_HANDLE_VALUE ? 0 : h;

#else
       char fm[3] = {0,0,0};

       char *mp = &fm[0];

       if (mode & utStream::SM_READ)
               *mp++ = 'r';
       else if (mode & utStream::SM_WRITE)
               *mp++ = 'w';
       *mp++ = 'b';
       fm[2] = 0;
       return fopen(filename, fm);
#endif
}
80% dela é para o manipulador no ambiente Windows (Windows não se divide em módulos bonitinhos e fáceis de organizar) oque não deve fazer muita diferênça já que a ideia é desviar para um manipulador SQLite.

Ok, deste ponto em diante é um stream de arquivo simples. Em teoria basta trocar as chamadas de arquivo por um manipulador de campo BLOB do SQLite (sqlite3_blob_open(), sqlite3_blob_read()...)

Até aqui parece humanamente fácil, pesquizei um pouco mais para ver se não havia como diminuir a possibilidade de criar buffers de dados em excesso. E aparentemente não tem sem ter um arduo trabalho que poderia se justificar em portes para o iPhone (por exemplo a segunda geração conta com 256MB de memória apenas por tanto não se pode esperar muito!)