{
"metadata": {
"name": ""
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Python para Desenvolvedores](http://ricardoduarte.github.io/python-para-desenvolvedores/#conteudo)\n",
"===================================\n",
"2ª edi\u00e7\u00e3o, revisada e ampliada\n",
"-----------------------------------\n",
"\n",
"Ap\u00eandice B: Blender\n",
"=============================\n",
"_____________________________\n",
"[Blender](http://www.blender.org/) \u00e9 um software de modelagem 3D de c\u00f3digo aberto, capaz de gerar anima\u00e7\u00f5es e tamb\u00e9m funciona como *Game Engine* (infraestrutura especializada para cria\u00e7\u00e3o de jogos para computador). Al\u00e9m disso, o software possui um renderizador interno e pode ser integrado a renderizadores externos, como os projetos, tamb\u00e9m de c\u00f3digo aberto, [Yafaray](http://www.yafaray.org/) e [LuxRender](http://www.luxrender.net/).\n",
"\n",
"![Tela do Blender](files/blender1.png)\n",
"\n",
"No Blender, uma cena \u00e9 composta por objetos, que precisam ser fixados em posi\u00e7\u00f5es e conectados a cena. Se o objeto n\u00e3o estiver conectado a cena, ele ser\u00e1 eliminado ao fim do processamento. Para cada s\u00f3lido, \u00e9 poss\u00edvel configurar v\u00e1rios materiais, que podem ter zero ou mais texturas.\n",
"\n",
"A cena padr\u00e3o do Blender \u00e9 composta por tr\u00eas objetos: uma c\u00e2mera, uma l\u00e2mpada e um cubo (representado como *mesh*). A escala no Blender \u00e9 medida em BUs (*Blender Units*).\n",
"\n",
"Com Python \u00e9 poss\u00edvel acessar todas essas estruturas do Blender atrav\u00e9s de m\u00f3dulos, incluindo:\n",
"\n",
"+ *Blender*: permite abrir arquivos, salvar e outras fun\u00e7\u00f5es correlatas.\n",
"+ *Object*: opera\u00e7\u00f5es com objetos 3D.\n",
"+ *Materials*: manipula\u00e7\u00e3o de materiais.\n",
"+ *Textures*: manipula\u00e7\u00e3o de texturas.\n",
"+ *World*: manipula\u00e7\u00e3o do ambiente da cena.\n",
"+ *Draw*: rotinas de interface com o usu\u00e1rio.\n",
"+ *Nmesh*: manipula\u00e7\u00e3o de malhas.\n",
"+ *BGL*: acesso direto as fun\u00e7\u00f5es do OpenGL.\n",
"\n",
"A API do Blender oferece v\u00e1rias texturas procedurais e uma cole\u00e7\u00e3o de s\u00f3lidos primitivos prontos, que podem ser criados e alterados atrav\u00e9s de c\u00f3digo.\n",
"\n",
"Exemplo de c\u00f3digo para a cria\u00e7\u00e3o de uma cena:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import math\n",
"import Blender\n",
"\n",
"# Pega a cena atual\n",
"cena = Blender.Scene.GetCurrent()\n",
"\n",
"# Elementos da cena \"default\"\n",
"camera = Blender.Object.Get()[0]\n",
"cubo = Blender.Object.Get()[1]\n",
"lamp = Blender.Object.Get()[2]\n",
"\n",
"# Move a c\u00e2mera\n",
"camera.setLocation(8., -8., 4.)\n",
"camera.setEuler(math.radians(70), 0.,\n",
" math.radians(45))\n",
"\n",
"# Muda a lente\n",
"camera.data.lens = 30\n",
"\n",
"# Remove da cena o objeto \"default\"\n",
"cena.objects.unlink(cubo)\n",
"\n",
"# Altera a intensidade da luz\n",
"lamp.data.energy = 1.2\n",
"\n",
"# Muda o tipo para \"Sun\"\n",
"lamp.data.type = 1\n",
"\n",
"# Aumenta o n\u00famero de samples\n",
"lamp.data.raySamplesX = 16\n",
"lamp.data.raySamplesY = 16\n",
"\n",
"# E a cor\n",
"lamp.data.col = 1., .9, .8\n",
"\n",
"# Cria outra fonte de luz\n",
"lamp1 = Blender.Lamp.New('Lamp')\n",
"lamp1.energy = 0.5\n",
"lamp1.col = .9, 1., 1.\n",
"_lamp1 = Blender.Object.New('Lamp')\n",
"\n",
"# Muda o lugar da fonte (default = 0.0, 0.0, 0.0)\n",
"_lamp1.setLocation(6., -6., 6.)\n",
"\n",
"# \"Prende\" a fonte de luz na cena\n",
"_lamp1.link(lamp1)\n",
"cena.objects.link(_lamp1)\n",
"\n",
"# Cria um material\n",
"material1 = Blender.Material.New('newMat1')\n",
"material1.rgbCol = [.38, .33, .28]\n",
"material1.setAlpha(1.)\n",
"\n",
"# Cria uma textura\n",
"textura1 = Blender.Texture.Get()[0]\n",
"textura1.setType('Clouds')\n",
"textura1.noiseType = 'soft'\n",
"textura1.noiseBasis = Blender.Texture.Noise['VORONOICRACKLE']\n",
"\n",
"# Coloca no material\n",
"material1.setTexture(0, textura1)\n",
"mtex1 = material1.getTextures()[0]\n",
"mtex1.col = .26, .22, .18\n",
"mtex1.mtNor = 1\n",
"mtex1.neg = True\n",
"mtex1.texco = Blender.Texture.TexCo['GLOB']\n",
"material1.mode += Blender.Material.Modes['RAYMIRROR']\n",
"material1.rayMirr = 0.2\n",
"material1.glossMir = 0.8\n",
"\n",
"# Cria o piso\n",
"mesh = Blender.Mesh.Primitives.Plane(40.)\n",
"piso = cena.objects.new(mesh,'Mesh')\n",
"piso.setLocation(0., 0., .05)\n",
"# Rotaciona o piso\n",
"piso.setEuler(0., 0., math.radians(45))\n",
"\n",
"# Coloca o material no piso\n",
"piso.setMaterials([material1])\n",
"piso.colbits = 1\n",
"\n",
"# Cria outro material\n",
"material2 = Blender.Material.New('newMat2')\n",
"material2.rgbCol = [.77, .78, .79]\n",
"material2.setAlpha(1.)\n",
"material2.mode += Blender.Material.Modes['RAYMIRROR']\n",
"material2.rayMirr = 0.6\n",
"material2.glossMir = 0.4\n",
"\n",
"# Coloca textura no outro material\n",
"material2.setTexture(0, textura1)\n",
"mtex2 = material2.getTextures()[0]\n",
"mtex2.col = .3, .3, .4\n",
"mtex2.mtNor = 1\n",
"mtex2.neg = True\n",
"mtex2.texco = Blender.Texture.TexCo['GLOB']\n",
"\n",
"mat = [material2]\n",
"\n",
"# Cria objetos na cena\n",
"def objeto(local, tam, mat, prim=Blender.Mesh.Primitives.Cube):\n",
"\n",
" mesh = prim()\n",
" obj = cena.objects.new(mesh, 'Mesh')\n",
" obj.setLocation(*local)\n",
" obj.size = tam\n",
" obj.setMaterials(mat)\n",
" obj.colbits = 1\n",
" return obj\n",
"\n",
"def coluna(x=0., y=0., z=0., mat=mat):\n",
"\n",
" # Cilindro\n",
" prim = Blender.Mesh.Primitives.Cylinder\n",
"\n",
" # Topo\n",
" local = x, y, 2.5 + z\n",
" tam = .25, .25, .1\n",
" objeto(local, tam, mat)\n",
"\n",
" # Base\n",
" local = x, y, 0. + z\n",
" objeto(local, tam, mat)\n",
"\n",
" # Corpo\n",
" for k in xrange(10):\n",
" local = x, y, .25 * k + z\n",
" tam = .2 - k / 200., .2 - k / 200., .25\n",
" objeto(local, tam, mat, prim)\n",
"\n",
"# Cria colunas no fundo\n",
"for i in xrange(16):\n",
"\n",
" # Primeira fileira\n",
" coluna(i - 8., 8)\n",
"\n",
" # Segunda fileira\n",
" coluna(-8., i - 8.)\n",
"\n",
" # Aqueduto\n",
" local = -8., i - 8., 3.\n",
" tam = .5, .5, .5\n",
" objeto(local, tam, mat)\n",
"\n",
" local = i - 8., 8., 3.\n",
" objeto(local, tam, mat)\n",
"\n",
"z = .25\n",
"\n",
"# Cria colunas em cima do piso\n",
"for i in (-3, 3):\n",
" for j in range(-2, 3):\n",
"\n",
" # Fileiras X\n",
" coluna(i, j, z)\n",
"\n",
" # Fileiras Y\n",
" coluna(j, i, z)\n",
"\n",
" # Cantos\n",
" for j in (-3, 3):\n",
" coluna(i, j, z)\n",
"\n",
"# Cria escada\n",
"for i in xrange(8):\n",
" local = 0., 0., i / 32. - .25\n",
" tam = 3.3 + (8. - i) / 8., 3.3 + (8. - i) / 8., .25\n",
" objeto(local, tam, mat)\n",
"\n",
"# Cria teto\n",
"for i in xrange(35):\n",
" local = 0., 0., 2.9 + i / 60.\n",
" tam = 3.5 - i / 200., 3.5 * ( 1. - i / 35.), .1\n",
" objeto(local, tam, mat)\n",
"\n",
"# Pega o \"mundo\"\n",
"world = Blender.World.Get()[0]\n",
"\n",
"# Modo \"blend\" no fundo\n",
"world.skytype = 1\n",
"\n",
"# Atualiza a cena\n",
"cena.update()"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sa\u00edda renderizada:\n",
"\n",
"![Blender](files/blender2.png)\n",
"\n",
"Exemplo com interface:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from math import *\n",
"from Blender.Draw import *\n",
"from Blender import BGL, Window, Mesh, Scene\n",
"\n",
"\n",
"class Buttons:\n",
" \"\"\"\n",
" Classe com para armazenar os bot\u00f5es para que\n",
" os valores possam ser usados por outras rotinas\n",
" sem usar vari\u00e1veis globais\n",
" \"\"\"\n",
" # Equa\u00e7\u00e3o\n",
" formula = Create('cos(x) - sin(3*x)')\n",
" # Varia\u00e7\u00e3o\n",
" delta = Create(.1)\n",
"\n",
"\n",
"def interface():\n",
" \"\"\"\n",
" Fun\u00e7\u00e3o que desenha a interface\n",
" \"\"\"\n",
" # Pega o tamanho da janela\n",
" x, y = Window.GetAreaSize()\n",
"\n",
" # Desenha caixa de texto\n",
" # Par\u00e2metros: r\u00f3tulo, evento, x, y, largura, altura, valor inicial,\n",
" # tamanho m\u00e1ximo do texto, tooltip\n",
" Buttons.formula = String('F\u00f3rmula: ', 0, 10, y - 30, 300, 20,\n",
" Buttons.formula.val, 300, 'Entre com uma express\u00e3o Python')\n",
" # Desenha caixa de n\u00famero\n",
" # Par\u00e2metros: r\u00f3tulo, evento, x, y, largura, altura, valor inicial,\n",
" # m\u00ednimo, m\u00e1ximo, tooltip\n",
" Buttons.delta = Number('Delta: ', 0, 10, y - 60, 300, 20,\n",
" Buttons.delta.val, .01, 1., 'Entre com a varia\u00e7\u00e3o')\n",
" # Desenha os bot\u00f5es\n",
" # Par\u00e2metros: texto do bot\u00e3o, evento, x, y, largura, altura, tooltip\n",
" PushButton('Ok', 1, 10, y - 90, 100, 20, 'Aplica as mudan\u00e7as')\n",
" PushButton('Fim', 2, 10, y - 120, 100, 20, 'Termina o programa')\n",
"\n",
" # Comandos OpenGL atrav\u00e9s da BGL\n",
" BGL.glClearColor(.7, .7, .6, 1)\n",
" BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)\n",
"\n",
"def events(evt, val):\n",
" \"\"\"\n",
" Fun\u00e7\u00e3o que responde a eventos diversos,\n",
" menos os gerados por bot\u00f5es\n",
" \"\"\"\n",
"\n",
" # Os eventos das teclas est\u00e3o definidas em Draw\n",
" if evt == ESCKEY:\n",
" # Termina o programa\n",
" Exit()\n",
"\n",
"def buttons(evt):\n",
" \"\"\"\n",
" Fun\u00e7\u00e3o que responde a eventos dos bot\u00f5es\n",
" \"\"\"\n",
"\n",
" if evt == 2:\n",
" Exit()\n",
"\n",
" elif evt == 1:\n",
" gen3d()\n",
"\n",
"def gen3d():\n",
"\n",
" # Cena 3D\n",
" cena = Scene.GetCurrent()\n",
" x = y = z = 0\n",
"\n",
" while x < 2 * pi:\n",
"\n",
" # Calcula os valores de z\n",
" z = eval(Buttons.formula.val)\n",
"\n",
" # Cria uma esfera de 16 segmentos, 16 an\u00e9is e 0.1 BU de raio\n",
" s = Mesh.Primitives.UVsphere(16, 16, .1)\n",
" esfera = cena.objects.new(s, 'Mesh')\n",
" \n",
" # Transfere a esfera para o local calculado\n",
" esfera.setLocation(x, y, z)\n",
"\n",
" # Pr\u00f3ximo x\n",
" x += Buttons.delta.val\n",
" \n",
" # Atualiza a cena\n",
" cena.update()\n",
"\n",
"if __name__ == '__main__':\n",
"\n",
" # Registra as fun\u00e7\u00f5es callback\n",
" Register(interface, events, buttons)"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Interface:\n",
"\n",
"![Blender](files/blender3.png)\n",
"\n",
"Sa\u00edda:\n",
"\n",
"![Blender](files/blender4.png)\n",
"\n",
"Exemplo de cria\u00e7\u00e3o de malha:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from Blender import NMesh, Redraw\n",
"\n",
"# Cria uma nova malha\n",
"mesh = NMesh.New()\n",
"\n",
"# Um dicion\u00e1rio para armazenar os v\u00e9rtices\n",
"# conforme a posi\u00e7\u00e3o\n",
"vertices = {}\n",
"\n",
"for X in xrange(3):\n",
" for Y in range(3):\n",
" # Uma face \u00e9 determinada pelos v\u00e9rtices que fazem parte dela\n",
" face = []\n",
" coords = [(X + 0, Y + 0), (X + 0, Y + 1),\n",
" (X + 1, Y+ 1), (X + 1, Y + 0)]\n",
"\n",
" # V\u00e9rtices da face\n",
" for x, y in coords:\n",
" vertices[(x, y)] = vertices.get((x, y), NMesh.Vert(x, y, 0))\n",
" face.append(vertices[(x, y)])\n",
"\n",
" # Adiciona um objeto \"face\" na lista de faces da malha\n",
" mesh.faces.append(NMesh.Face(face))\n",
"\n",
"# Adiciona os v\u00e9rtices na malha\n",
"for vertice in vertices.values():\n",
" mesh.verts.append(vertice)\n",
"\n",
"# Carrega a malha na cena\n",
"NMesh.PutRaw(mesh, 'chess', True)\n",
"Redraw()"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sa\u00edda:\n",
"\n",
"![Malha no Blender](files/blender5.png)\n",
"\n",
"Para executar c\u00f3digo em Python no ambiente do Blender, basta carregar o programa na janela de editor de texto do Blender e usar a op\u00e7\u00e3o de execu\u00e7\u00e3o no menu.\n",
"\n",
"Game engine\n",
"-----------\n",
"*Game engine* \u00e9 um software que facilita a cria\u00e7\u00e3o de jogos, simulando determinados aspectos da realidade, de forma a possibilitar a intera\u00e7\u00e3o com objetos em tempo real. Para isso, ele deve implementar v\u00e1rias funcionalidades que s\u00e3o comuns em jogos, como por exemplo a capacidade de simula\u00e7\u00e3o f\u00edsica. O objetivo principal do uso de *game engines* \u00e9 centrar o foco da cria\u00e7\u00e3o do jogo no conte\u00fado, ou seja, mapas (n\u00edveis), personagens, objetos, di\u00e1logos, trilha sonora e cenas. \u00c9 comum que v\u00e1rios jogos usem o mesmo engine, reduzindo assim, o esfor\u00e7o de desenvolvimento.\n",
"\n",
"Um dos principais recursos fornecidos por *game engines* \u00e9 a capacidade de renderizar cenas em 2D ou 3D em tempo real, geralmente usando uma biblioteca gr\u00e1fica, como o OpenGL, permitindo anima\u00e7\u00f5es e efeitos especiais. O componente especializado para esta fun\u00e7\u00e3o \u00e9 conhecido como *render engine*.\n",
"\n",
"Al\u00e9m disso, a simula\u00e7\u00e3o f\u00edsica tamb\u00e9m \u00e9 essencial para um jogo, para representar de forma adequada os movimentos dos personagens sendo influenciados pela gravidade, in\u00e9rcia, atrito, detec\u00e7\u00e3o de colis\u00f5es e outros. O componente que realiza esses c\u00e1lculos \u00e9 chamado *Physics Engine*.\n",
"\n",
"Outra funcionalidade importante \u00e9 a l\u00f3gica, que \u00e9 como o jogo determina o comportamento do ambiente e dos personagens. Em muitos casos, o *game engine* suporta uma ou mais linguagens para descrev\u00ea-la.\n",
"\n",
"Os *game engines* podem incluir outros recursos importantes para determinados tipos de jogos, como conectividade. No caso de MMOG (*Massively Multiplayer Online Games*), que s\u00e3o muito complexos, a infraestrutura de software \u00e9 mais conhecida como *middleware*.\n",
"\n",
"A populariza\u00e7\u00e3o dos *game engines* aconteceu durante a d\u00e9cada de 90, gra\u00e7as a Id Software, que desenvolveu os jogos que definiram o g\u00eanero chamado FPS (*First Person Shooter*), jogos de a\u00e7\u00e3o em primeira pessoa. Esses t\u00edtulos tiveram seus *engines* licenciados para outras empresas, que criaram outros jogos desenvolvendo o conte\u00fado do jogo. Em paralelo, os processadores de v\u00eddeo foram incorporando suporte as fun\u00e7\u00f5es gr\u00e1ficas cada vez mais sofisticadas, o que facilitou a evolu\u00e7\u00e3o dos *engines*. A Id tamb\u00e9m liberou os game engines das s\u00e9ries DOOM e Quake em GPL.\n",
"\n",
"Al\u00e9m de entretenimento, outras \u00e1reas podem se beneficiar desses engines. Chamadas genericamente de serious games, aplica\u00e7\u00f5es nas \u00e1reas de treinamento, arquitetura, engenharia, medicina e marketing est\u00e3o se popularizando aos poucos.\n",
"\n",
"O Blender inclui um *game engine* gen\u00e9rico, que permite a cria\u00e7\u00e3o de jogos 3D, usando o pr\u00f3prio aplicativo para cria\u00e7\u00e3o de conte\u00fado e Python para as partes com l\u00f3gica mais complexa.\n",
"\n",
"O Blender Game Engine (BGE) usa como *physics engine* o projeto, tamb\u00e9m *Open Source*, chamado [Bullet](http://www.bulletphysics.org/). Com ele, \u00e9 poss\u00edvel simular o comportamento de corpos r\u00edgidos (como pe\u00e7as de maquinaria), macios (como tecidos), est\u00e1ticos (fixos) e intang\u00edveis (que n\u00e3o s\u00e3o afetados por colis\u00f5es).\n",
"\n",
"![Blender Bullet](files/blender6.png)\n",
"\n",
"O *render engine* do Blender suporta GLSL (*OpenGL Shading Language*), o que permite que ele utilize recursos avan\u00e7ados dispon\u00edveis nos processadores de v\u00eddeo mais recentes.\n",
"\n",
"J\u00e1 a l\u00f3gica \u00e9 definida no BGE atrav\u00e9s de *Logic Bricks*, que segue um modelo baseado em eventos. Eventos s\u00e3o associados a um objeto da cena e podem ser gerados por perif\u00e9ricos de entrada (como teclado e mouse), pelo sistema (tempo), pelo pr\u00f3prio BGE (colis\u00f5es, por exemplo) ou por mensagens enviadas por outros objetos. Quando um ou mais eventos s\u00e3o detectados, o BGE toma uma decis\u00e3o e reage de acordo.\n",
"\n",
"Existem tr\u00eas tipos de *bricks*:\n",
"\n",
"+ Sensores (*sensors*), que detectam os eventos.\n",
"+ Controladores (*controllers*), que relacionam os sensores com os ativadores adequados.\n",
"+ Ativadores (*actuators*), que ativam as rea\u00e7\u00f5es.\n",
"\n",
"No painel *Logic*, as associa\u00e7\u00f5es entre os *bricks* pode ser definida de forma interativa. O BGE tem diversos ativadores prontos, para realizar tarefas como encerrar a execu\u00e7\u00e3o ou mudar a velocidade do objeto.\n",
"\n",
"O BGE pode evocar c\u00f3digo em Python para responder aos eventos, atrav\u00e9s do controlador \u201cPython\u201d. Quando uma fun\u00e7\u00e3o em Python \u00e9 executada, ela recebe como argumento o controlador que realizou a chamada, com isso \u00e9 poss\u00edvel identificar e modificar o objeto (*owner*) que possui o controlador.\n",
"\n",
"Exemplo (m\u00f3dulo com fun\u00e7\u00e3o para teleporte):"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# M\u00f3dulo de interface com o Game Engine\n",
"import GameLogic\n",
"\n",
"def teleport(cont):\n",
"\n",
" # obt\u00eam o dono do controller\n",
" own = cont.owner\n",
"\n",
" # obt\u00eam a cena\n",
" scene = GameLogic.getCurrentScene()\n",
" \n",
" # obt\u00eam o destino\n",
" dest = scene.getObjectList()['OBr_portal']\n",
" \n",
" # obt\u00eam as coordenadas do destino\n",
" x, y, z = dest.getPosition()\n",
" \n",
" # move a c\u00e2mera para 1 BU acima do destino\n",
" own.setPosition([x, y, z + 1])"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Essa fun\u00e7\u00e3o muda a posi\u00e7\u00e3o do objeto, para um BU a cima do objeto chamado \u201cr_portal\u201d, independente do lugar na cena em que estiver localizado."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"\n",
""
],
"output_type": "pyout",
"prompt_number": 1,
"text": [
""
]
}
],
"prompt_number": 1
}
],
"metadata": {}
}
]
}