"3DCS avagy sebészeti tervezés"
Részletek a hírekben!
 
   
 
 
Naprakész hírek...                                         240 fórumozó...                                         Számos magyar oktatóanyag...                                         Videóanyagok magyarul is...                                         Letölthető fájlok...                                         Textúrák...                                         Összegyűjtött linkek...
 
  Rólunk
Hírek
Fórum
Kihívás
Kapcsolatok
Galéria
Aktuális, 2015
2014, 2013
2012, 2011
2010, 2009
Leírások
Kezdő leckék
Haladó leckék
Egyéb leírás
Billentyűk
Videóanyagok
Kezdő videók
Haladó videók
Egyéb videók
Tippek, trükkök
Letöltés
BLEND fájlok
Textúrák
Linkajánló
Archivum
Projektek
BlendRace SokoTruck
Flipper MB game
 
 

Blender leírások és oktató anyagok

 

Szerző: raville (ragdavill kukac yahoo.fr)

Utolsó módosítás dátuma: 2006/08/09 03:00

Python&Blender

Titkos üzenet, és amit tudunk róla

Ddd Max üzenetet küldött neked, amiért Blendert használsz. Üzenetével csak egy baj van, hogy valamilyen fura oknál foga elkódolta. Ránk vár a feladat, hogy kibontsuk a levelet (ami egy blend fájl) és megfejtsük tartalmát...

Az üzenetben egy scene (szín) van, és rengeteg hasonló objektum. Néhány közülük valószínűleg az üzenet darabjainak hordozója, a nagy többség viszont csak szemét. A szakértők azt állítják, hogy ki lehet szűrni azokat az objektumokat, amelyekre az üzenet megfejtéshez szükség van. Néhány test mintha el lenne forgatva, meg sorszámozva is vannak...


1. Így néz ki az üzenet

Jó ronda, mi? Amíg a kutatók a további információkat és a köztük lévő kapcsolatokat keresik, nekünk az a feladatunk, hogy felkészüljünk a legrosszabbra és megtanuljuk, hogyan lehet Blender szkriptet írni, mert bizonyára szükségünk lesz erre a tudásunkra a probléma megoldásához.

Először nézzünk át egy-két dolgot, hogy python ismereteink elegendőek legyenek a feladathoz.

Szekvencia típusok: indexelhetőség, szeletelés

A szekvencia alapú típusok (lista, szöveg, tuple,..) rendelkeznek néhány hasznos funkcióval (valójában ettől lesznek szekvencia alapúak..), pl. az elemek indexelhetőségével. Ezt talán már említettem korábban, de többet érdemel annál, minthogy csak lábjegyzetben hivatkozzam rá.

>>> l=["Már","megint","itt","van","a","szerelem"]
>>> print l[0]		#0., azaz legelso elem kiiratása
Már
>>> print l[len(l)-1]	#utolso elem kiiratása
szerelem
>>> l[2]="hol"		#2. elem csereje
>>> l[5]+="?"		#5. elem csereje (uj szoveg kepzesevel)
>>> mondat=''.join(l)	#a lista reszeit ertlemes szovegge rakjuk ossze, a join a szoveg tipus metodusa
>>> print mondat
Már megint hol van a szerelem?

1. példa

Tehát az egyes elemeket direkten is elérhetjük, ha ismerjük a pontos pozíciójukat a listán belül. Figyeljünk oda, hogy az legelső elem listabeli pozíciója a 0, és nem az 1. A lista mutable típus, ezért az indexelve hivatkozott elemeken változtathatunk is. Ez szövegek (és más immutable, szekvencia típusok) esetén nem működik.

>>> s="Működni fog?"
>>> print s[11]
?
>>> s[11]="!"		#kerdojel csereje
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: object doesn't support item assignment

2. példa

Hiába erőlködünk, ez nem fog menni.

A másik hasznos funkció, a szelet-képzés (slicing) arra jó, hogy kijelölve az adatsorozat egy részét, csak azon végezzünk el valamilyen műveletet.

>>> l=['a','b','c','d','e']
>>> print s[:1]	#az elejetől az 1 indexu elemig 
['a','b']
>>> print s[2:]	#a 2 indexu elemtol a vegeig
['c','d','e']
>>> a,b = 2,3		#a=2 és b=3
>>> print s[a:b]	#a 2 indexu elemtol a 3 indexuig
['c','d']
>>> print s[-2:]	#az utolso ket elem
['d','e']

3. példa

A kettőspont elé a részlista kezdő, mögé pedig utolsó elemének indexét kell írni. A negatív értékek hátulról számítanak (de a 0 ugyanaz, mint a -0). Néha igen kényelmesé válik szeletek használatával a programozás.

És még egy trükk, miképpen tudunk egyszerűen listákat másolni.

>>> l1=['a','b']
>>> l2=l1[:]			#ez valodi masolas, nem pedig referencia-atadas.. (lasd ertekadas fejezet)
>>> l2.append('c')
>>> print l1, l2		#l2 más tartalmaz, mint l1 
['a','b'] ['a','b','c']

4. példa

Lista típus: metódusok

Valószínűleg már érezzük, listák nélkül nem élhetünk meg python környezetben. Fussunk gyorsan végig a típushoz tartozó metódusokon. Némelyiket már ismerjük:

lista.append(elem): a megadott objektumot a lista végére rakja

lista.count(elem): visszatérési értéke az elem előfordulásainak száma a listában

lista.extend(seqtipus): elemek listához való hozzáfűzése bármely más szekvencia (lista,tuple..) típusból

lista.index(elem): a visszatérési érték a megadott objektum első előfordulásának indexe, azaz pozíciója a listában

lista.insert(index, elem): az elem megadott index pozíció elé való beillesztése

lista.pop(n): az n. elem kivétele, ha nem adunk meg paramétert, akkor az utolsót veszi ki

lista.remove(elem): elem eltávolítása a listából (csak az első előfordulását törli)

lista.reverese(): lista megfordítása, így lesznek az elsőkből utolsók

lista.sort(): lista rendezése, legkisebbek előre, nagyok hátra

Nem sokára használnunk is fogjuk a legfontosabbakat.

tuple adattípus

A tuple, habár immutable típus, sokban hasonlít a listákhoz. Bármit belerakhatunk, elemein végiglépdelhetünk, lekérdezhetjük őket indexük alapján, és szeletelhetünk is kedvünkre. Ellenben, ha egyszer létrehoztunk egy tuple-t, többé nem változtathatunk az elemszámán, és a bele helyezett objektumokat sem cserélhetjük le! E megszorítások jótékony hatása, hogy az adatstruktúra erőforráskímélőbb és gyorsabban működik, mint a lista típus.

Gömbölyű zárójelek jelzik, ha ilyen típussal van dolgunk. Néhány példa:

>>> a=(1,3,"Halál a májra!")
>>> print a
(1,3,"Halál a májra!")
>>> print type(a)

>>> print a[1]	
3
>>> a[1]=4.567		#probaljunk meg egy elemet lecserelni
  File "", line 1, in ?
TypeError: object doesn't support item assignment

5. példa

Az utolsó próbálkozás csúfos kudarcot vallott. Szerencsére, ha már magán a tuple-n nem is változtathatunk, az elemein viszont igen, de csak akkor, ha azok mutable típusúak:

>>> a=([1,2],[3,4])				#a lista mutable
>>> a[0].append("Coco Jumbo, ajajajj..")	#az elso elem modositasa
>>> print a
([1,2,"Coco Jumbo, ajajajj.."],[3,4])

6. példa

Továbbá, rendelkezik az packing/unpacking (csomagolás/kibontás) képességgel is.

>>> b=1, 3, "Paris"	#packing, egy tuple definialasa, zarojelek nelkul
>>> print type(b)

>>> x,y,z=b		#unpacking
>>> print z,x,y	#a bal oldali valtozokba kerult a három tuple-beli ertek
Paris 1 3

7. példa

Listák esetén is működik a kibontás, próbáld ki. A csomagolás viszont mindig tuple típust eredményez. Bizonyos feladatokhoz csak tuple-kat használhatunk (pl. szövegek formázása, lásd később).

#min/max függvények

A min beépített függvény a paraméterei között megadott értékek közül a legkisebbet adja vissza. Ez számoknál egyértelmű, viszont más adattípusokra is működik.

>>> print min(1,2,3,4,5)
1
>>> print min("a","b","c","d")		#szovegek esetén is ertelmezheto
a
>>> print min("a","A")			#a nagy betuk elorebb vannak 
A
>>> print min("ab","aB")		#tobb betut tartalmazó szovegek eseten...
aB
>>> print min("aba","aBw")		#az elso kulonbozo betu számit...
aBw

8. példa

Szövegek, listák, tuple-k esetén is használhatjuk a függvényt. Ilyenkor a legkisebb elem értékével tér vissza.

>>> print min( "bcdefga" )		#szoveg
a
>>> print min( [1,2,3,4,5] )		#lista
1
>>> print min( ("a","b","c","d") )	#tuple
a
>>> print min( [["a","A"], ["a","B"]] ) #egy listaban ket lista
['a', 'A']

9. példa

Mi van akkor, ha különböző típusokat adunk meg, vajon melyik lesz kisebb? Vizsgáljuk meg ezt a kérdést egy rövid kód segítségével..

   1. def mi_a_sorrend(a): #lista a bemeno parameter
   2. while a: #amig van elem a-ban
   3. b=min(a)
   4. print b
   5. a.remove(b) #toroljuk a listabol b-t
   6. #fv vege
   7.
   8. mi_a_sorrend( [1 , "a" ,"A" , 0.2 , 1.2, None, ["majom", 234, [] ] , (0.0, 0.11)] )

10. példa

A függvény egy ciklust tartalmaz, amely a paramétereként megadott listából kiválasztja a legkisebb elemet, kiírja, majd törli is belőle. Előbb-utóbb elfogynak az elemek, és a ciklus leáll, hiszen az üres lista False értékű, így a while feltétele hamissá válik.

Az eredmény:

None
0.2
1
1.2
['majom', 234, []]
A
a
(0.0, 0.11)

Hmm, nehéz lett volna kitalálnunk magunktól, azt hiszem. =]

A max hasonlóan működik, csak nem a legkisebb, hanem a legnagyobb értéket választja ki.

Blender előkészítése

Eljött az idő, vegyük végre birtokba a felületet, amit használni fogunk. Úgy kell indítanunk a Blendert, hogy később láthassuk az üzeneteket, amelyeket az alapértelmezett kimenetre küld. Indításnál általában egy konzol ablak is nyílik a főablak mellett, erre állandóan szükségünk lesz (ha virtuális terminálból indítod, akkor oda írja a dolgokat).

Megjegyzés: Létezik egy fejlesztés, amely megpróbálja a Blenderen belül megjeleníteni a kimenetet. Én nem próbáltam, de állítólag még vannak vele problémák. Remélhetőleg hamar elkészül, és használhatjuk, mert szükség lenne rá.

Ha 2.40-es, vagy annál frissebb Blender verziót használsz az alapértelmezett screen beállításokkal, akkor a leggyorsabb módja annak, hogy egy szövegszerkesztő ablak elé kerülj, hogy beállítod az utolsó munkaképernyőt (a screen helyett ezt a szót fogom használni). A ++ billentyű kombináció háromszori lenyomása egyből odarepít minket (kivéve, ha egy gonosz ablakkezelő ezt megakadályozza), de a User Preferences ablak fejlécén (alapesetben ez a Blender legfelső sávja) is kiválaszthatjuk a kívánt munkafelületet az első legördülő listából.


2. Screen váltó legördűlő doboz

Ha minden kötél szakad, felezzük meg a képernyőt, és nyissunk egy új Text Editor ablakot.

A szövegszerkesztő ablak elég egyszerű, de a legfontosabb képességekkel (talán) rendelkezik. A láblécén lévő ikonokkal ki-be kapcsolhatjuk a szintaxis kiemelést és a sorok számozását.


3. Szintaxis kiemelés és sorok számozása gombok

Szerintem kapcsoljuk be mindkettőt, ugyanis hasznos dolgok.

Teszteljük le a rendszert! Írassunk ki valamit, legyen egy egyszerű "Hello Blender".

   1. print "\nHello Blender" 

11. példa

Futtassuk az egy soros szkriptet, azaz nyomjuk meg az + gyorsbillentyűt. Ez csak akkor működik, ha a éppen a szerkesztőablak áll fókuszban (az egér felette van)! Egyébként az ablak File menüjében találjuk a Run Python script parancsot.

Keressük meg, hova írta ki a program a szöveget. Ha megtaláltuk, akkor tovább is léphetünk.

Felfedező körút, az Object almodul

Minden Blender szkript általában a Blender python moduljainak betöltésével kezdődik. Ezek teszik lehetővé, hogy szkriptjeink kommunikálni tudjanak a programmal. A fő modult (valójában ez egy package, de mindegy) Blender-nek hívják. Ezt mindig be kell töltenünk, ezen keresztül érhetjük az almodulokat. Almodulból rengeteg van, ez az elején kicsit rémisztő lehet.

   1. import Blender #Blender package betoltese, csak Blenderen belul erhető el!
   2.
   3. print "\n",dir(Blender)

12. példa

Eredményként a Blender package azonosítóit, az almodulok listáját kapjuk, ha futtatjuk a kódot. A Blender python API referenciában (v2.42) minden modul leírását megtaláljuk. Sokat kell majd forgatni a lapjait, de minden benne van, aminek egy referenciában benne kell lennie. Ezen kívül a meglévő szkriptek olvasgatása is elég hasznos a tanulás szempontjából, rengeteget találhattok az interneten (is).

Egyelőre csak az Object almodult (ref) vegyük szemügyre. Az Object-en keresztül a térben lévő objektumokhoz nyerhetünk hozzáférést. Kérdezzük le, milyen objektumok ácsorognak a virtuális térben (bármelyik színen)!

   1. import Blender
   2. from Blender import Object #az almodul nevterenek beemelese
   3.
   4. objektumok=Object.Get()
   5. print objektumok

13. példa

Természetesen, a szkript csak akkor fog kiírni valamit, ha van objektum valamelyik színen! Nálam a következő eredménye lett a futtatásnak:

[[Object "Camera"], [Object "Cube"], [Object "Lamp"]]

Az Object almodul Get (ref) függvénye listázza a színen lévő objektumokat. A kimeneten láthatjuk, hogy a Blender objektumok is szögletes zárójelben jelennek meg. A fenti eredmény tehát egy objektumokat tartalmazó listát, nem pedig három, listába ágyazott listát jelent!

A második sorban látható paranccsal lehet egy almodult betölteni egy package-ből. Ha egy sima python modulból akarunk egy azonosítót átemelni a globális névtérbe, akkor is ezt a szintaxist kell használnunk. Ez legtöbbször csak arra jó, hogy megkönnyítsük a dolgunkat, és ne kelljen annyit írni. Példák:

   1. import Blender
   2. print Blender.Object.Get() #igy tul hosszan kell ra hivatkozni...
   3.
   4. from Blender import Object #az Object azonosito (jelen esetben almodul) nevterbe emelese
   5. print Object.Get() #mar ez is mukodik, az Object azonosito ismert
   6.
   7. from math import sqrt #a gyokvonás fuggvény beemelese
   8. print sqrt(4) #mar nem kell megjelolnunk, hogy a math nevterben van
   9.
  10. from math import * #minden azonositot beemel a math-bol
  11. print pow(3,2) #3 masodik hatvanya = 9, már a math-ban levo pow is ismert azonosito

14. példa

Object osztály

Korábban már pedzegettem, hogy minden python objektumhoz tartozik egy osztály. Minden osztály egy adattípust határoz meg. Az osztály definíciója azt írja le, hogy az adott adattípus mire képes, milyen adatmezői, metódusai, azaz attribútumai vannak. E definíció alapján jönnek létre az objektumok, amelyek végül az tényleges adatokat reprezentálják a memóriában.

Például, egy egész számot reprezentáló objektummal mindig ugyanazokat a műveleteket végezhetjük el, bármi is legyen konkrétan az általa képviselt szám (jó, nullával osztani tudjuk, hogy nem lehet, de megpróbálni azért igen =]). Ugyanez vonatkozik a blenderen belüli objektumokra is. Például minden 3D alakzat rendelkezik pozícióval, elforgatható a térben és anyagtulajdonság kapcsolható hozzá. Ezen képességek és funkciók leírását mind az őket meghatározó osztályok tartalmazzák.

A térbe kirakható objektumok mind egy közös osztály reprezentánsai, az Object osztályé (ref). Ennek definícióját is az Object modul tartalmazza (igen, kicsit sok az Object, de majd megszokod). A referenciában megtalálod az összes metódust és adatmezőt, ami hozzá tartozik, szépen sorban. Kicsit hosszú a lista, de előbb-utóbb mindenki ráérez, hogy miképpen keressen benne, csak egy kis gyakorlat és türelem kell hozzá.


4. Object osztály referencia részlet

A fenti képernyőképen az Object osztály dokumentációjának egy részlete látható. Olvassuk el, mire jó a loc adatmező! Állítólag az objektum pozícióját tartalmazza (vektor formájában). Egy teszt nem árt, hátha félreértünk valamit (fránya idegen nyelvek).. írassuk ki ezt az értéket minden létező objektum esetén!

   1. import Blender
   2. from Blender import Object
   3.
   4. print "\n"
   5. for ob in Object.Get():
   6. print "Name: ", ob.name ,"\t Pos: %.2f %.2f %.2f" % ob.loc #nev es pozicio kiiratasa
   7. #cikl vege

15. példa

Name:  Camera    Pos: 7.48 -6.51 5.34
Name:  Cube      Pos: 0.00 0.00 0.00
Name:  Lamp      Pos: 4.08 1.01 5.90

Könnyen ellenőrizheted az adatokat a Transform Properties panelen, de azt hiszem, nincs rá szükség ;)

Csupán a 6. sor érdemel némi magyarázatot a szkript forráskódjában. Az ob.name attribútum az objektum nevét tartalmazza, a \t pedig egy egyszerű tabulátor jel, a kimenet formázása kedvéért (így a Pos: feliratok egy oszlopba kerülnek). Amit utána láttok, az szintén a formázás kedvéért van.

Egy térbeli pozíció három koordinátából áll, az X/Y/Z komponensekből. Ezeket általában egybe kezelik, vektorként. Az ob.loc egy tuple típusú érték, benne található a három valós (float típusú) szám, amely az objektum pozícióját leírja. A futás során az interpreter behelyettesíti a %.2f formázó kódok helyére ezt a három értékét.

Formázó kódokkal befolyásolhatjuk az adatok szöveges típusra való konvertálását. Jelen esetben azt adtuk meg, hogy csak két tizedes jegyig írja ki a valós számokat (mivel egy sor túl hosszú lenne, nem férne el egy sorban). A konvertálandó értékeket a szöveg végére kell rakni, egy százalékjel mögé. Több adat esetén gömbölyű zárójelet kell használnunk*, amelyben az értékek vesszővel vannak elválasztva (azaz egy tuple-t). Az interpreter a megadott sorrendben helyettesíti be ezeket az értékeket.

* Vagy kapcsos zárójeleket, leképzés esetén. Egyszer biztos lesz róla szó...

>>> s="%f + %f = %.3f" % (3.14,3.15,6.29)
>>> print s
'3.140000 + 3.150000 = 6.290'

16. példa

Jegyezd meg, hogy a formázásnál nem használhatunk tuple helyett listát (sajnos)!

Ha majd igény lesz rá, akkor bővítjük az ismereteinket a formázott szövegek témakörében is, ez most csak egy rövid kitekintés volt.

Térjünk vissza a blenderes példára, konkrétan az ob.loc-ra. A loc tuple minősége természetesen nem azt jelenti, hogy egy objektum pozícióját nem tudjuk megváltoztatni. A Blender útjai elég kacskaringósak, és bizonyára sok redundancia előfordul bennük. A setLocation metódus és a LocX/LocY/LocZ attribútumok mind használhatóak pozíció módosítása céljából, viszont lekérdezni gyorsabb a loc-ot.

Függvények, attribútumok az Object modulban és osztályban

Van még egy rakás egyéb attribútum is a loc-on kívül. Ahogy szükséged lesz rájuk, úgy fogod őket megismerni. Bizonyára a legtöbbel soha nem fogsz találkozni, ezért felesleges külön végigvenni őket. A következő felsorolásban bemutatok néhányat, mind a modulból, mind pedig az osztályból választottam egy-két metódust.

Object modul:

Get(): meglévő objektumok lekérdezése

GetSelected(): kijelölt objektumok lekérdezése

New(): új objektum létrehozása

Object osztály:

getLocation() / setLocation(): pozíció lekérdezése, módosítása

getEuler() / setEuler(): elforgatottság lekérdezése, módosítása (hogy miért Euler, arra majd visszatérünk)

getSize() / setSize(): méret lekérdezése, módosítása

getName() / setName(): objektum nevének lekérdezése, módosítása

isSeleted() / select(): objektum kijelöltségének lekérdezése, módosítása

getType(): objektumhoz kapcsolt adatblokk (blenderbeli) típusának lekérdezése (Mesh, Lamp, stb..)

Megvan a titok kulcsa

Miközben ismereteinket bővítettük, a laborban megfejtették, hogyan lehet kikódolni az üzenetet! Az üzenetet tartalmazó fájlt innen töltheted le.

Kiderült, hogy az objektumok nevei tartalmazzák az információt, az üzenet szövegét. Minden objektumban pontosan egy betűt találunk. A nevek valahogy így néznek ki: bob_szám_betű. De az is kiderült, hogy nem mindegyik objektum tartalmaz valódi üzenetrészletet, hanem csak azok, amelyek el lettek forgatva!

Önálló feladat (1)

A feladat tehát adott, fejtsük meg az üzenetet. Az objektumok neveit (nev=obj.name) egy (for) ciklusban kérdezzük le, daraboljuk fel (nev.split("_")), és közben a testek elforgatottságát is ellenőrizni tudjuk. Ha elforgatott testtel állunk szemben, akkor megjegyezzük a betűt, amit a nevében hordoz. Úgy néz ki, hogy az objektumok sorrendje pont jó, azaz ha egyszerűen végigfutunk rajtuk, akkor a jó sorrendben kapjuk meg a betűket.

A getEuler metódus (ref) egy vektort ad vissza. A vektor három komponense az objektum elfotgatottságának mértékét mutatja a három koordinátatengely körül, szögekben. Ha ezek valamelyike nem zérus, az objektum elforgatott állapotban van.

Önálló (és nehezebb) feladat (2)

Írd meg úgy az szkriptet, hogy nem hagyatkozol az objektumok eredendően jó sorrendjére, hanem a nevekben található számok alapján rakod össze az üzenetet! Minél kisebb egy szám, annál előrébb van a hozzá tartozó betű az üzenetben.

Egy megoldási vázlat:

Az első ciklusban külön listába gyűjtjük az üzenet betűit és a hozzá tartozó számokat (a eredeti, rossz sorrendet mindkét listában megtartva; ha az append metódust használjuk, nem lesz gond). Mind a két lista ugyanolyan hosszú lesz. A második ciklus minden lépésében kiválasztjuk a legkisebb számot a sorszámokat tartalmazó listából (elem=min(lista))), majd megkeressük az így kiválasztott elem indexét (index=lista.index(elem)), és a másik lista megfelelő pozícióján csücsülő betűt hozzárakjuk a változóhoz, amelyikben az üzenetet építjük. Végül, a ciklus utolsó műveleteként, mind a két listából eltávolítjuk az adott indexen lévő elemet (lista.pop(index)).

A listák elemei előbb-utóbb elfogynak, és a második ciklus végeztével összeáll az üzenet.

Megjegyzés: Később, ha már többet tudunk, egyszerűbb lesz ennek a feladatnak a megoldása is. Viszont, ha van csak listákat használó, de egyszerűbb ;) megoldásod, ne fogd vissza magad, és oszd meg velünk... biztos, hogy van.

Happy scripting!

Ha kérdésed van, gyere a fórumba. Ha úgy érzed, hogy valamelyik feladatot nem megy (esetleg nem találod a módját, miképpen kezdj hozzá), akkor mindenképpen gyere, és segítünk, ami egyenlő azzal, hogy bebizonyítjuk, magadtól is meg tudod csinálni. ;)