Давайте пограємо зі змією/Скриптуємо Блендер: відмінності між версіями

Вилучено вміст Додано вміст
DannyS712 (обговорення | внесок)
м <source> -> <syntaxhighlight> (phab:T237267)
Рядок 2:
Якщо ми хочемо просунутись до програмування трьохвимірних ігор, нам обов'язково треба включити в свій арсенал хоч якийсь редактор. Бо навіть простенька модель кубика, якщо її малювати вручну, з коду буде виглядати так:
 
<sourcesyntaxhighlight lang="cpp">
glBegin(GL_POLYGON);
glVertex3f(1.000000,1.000000,-1.000000);
Рядок 44:
glVertex3f(-1.000000,1.000000,1.000000);
glEnd();
</syntaxhighlight>
</source>
 
А в ньому всього то шість граней. І то я полінувався вводити все вручну, і згенерував цей кубик скриптом з блендера. Тепер порахуйте скільки граней у цієї лисої пані:
Рядок 66:
Щоб мати поняття що ми хочемо зробити можна подивитись [http://www.youtube.com/watch?v=PdHS2Unt6Js коротке трихвилинне відео].
 
<sourcesyntaxhighlight lang="python">
#!BPY
 
Рядок 86:
 
Blender.Window.FileSelector(write, "Export")
</syntaxhighlight>
</source>
 
 
Рядок 137:
 
Спочатку знайти вибраний об'єкт:
<sourcesyntaxhighlight lang="python">
sce = bpy.data.scenes.active
ob = sce.objects.active
</syntaxhighlight>
</source>
 
Потім отримати його меш:
<sourcesyntaxhighlight lang="python">
mesh = ob.getData(mesh=1)
</syntaxhighlight>
</source>
 
Хоча ми могли і отримати не меш. Якщо наприклад вибраним об'єктом було джерело світла. А воно не має ніякої трикутної сітки. Тому, в коді що піде далі варто ловити виключення. Якось так:
<sourcesyntaxhighlight lang="python">
try:
for face in mesh.faces:
Рядок 158:
except:
print "Selected something without mesh. Nothing exported."
</syntaxhighlight>
</source>
Тобто від пробує виконати код в секції try, а якщо не виходить, то жаліється в консоль. Варто зауважити, що при тестуванні скриптів до блендера, бажано щоб він був запущений з консолі, тоді в ній будуть з'являтись повідомлення про помилки. Він вам прямо так і скаже: [[Файл:Python error check console.png|150px]], але не скаже де ту консоль шукати, тому вам варто знати самим.
 
Рядок 170:
Тепер давайте намалюємо свій меш. Що небудь попростіше. Найпростіше - трикутник, з нього і почнемо. Ось функція яка його створює:
 
<sourcesyntaxhighlight lang="python">
def createtriangle():
obj=NMesh.GetRaw()
Рядок 190:
 
NMesh.PutRaw(obj,"Triangle",1)
</syntaxhighlight>
</source>
 
А тепер по порядку. Спочатку ми створюємо базовий об'єкт, з чистим мешем:
 
<sourcesyntaxhighlight lang="python">
obj=NMesh.GetRaw()
</syntaxhighlight>
</source>
 
Якщо цій функції передати параметром ім'я якогось існуючого об'єкта, то меш того об'єкта буде взятий за основу. Тобто побудова почнеться не з нуля. Тільки варто переконатись що такий об'єкт існує. Далі створимо якісь три вершини. Будь-які, основне - аби їх було не менше трьох, бо інакше ми не отримаємо жодної грані.
 
<sourcesyntaxhighlight lang="python">
v1=NMesh.Vert(0,10,0)
v2=NMesh.Vert(-1,0,0)
v3=NMesh.Vert(1,0,0)
</syntaxhighlight>
</source>
 
Потім додамо ці вершини у об'єкт.
 
<sourcesyntaxhighlight lang="python">
obj.verts.append(v1)
obj.verts.append(v2)
obj.verts.append(v3)
</syntaxhighlight>
</source>
 
Коли вершини створено, можна створювати грані. Починають з порожньої грані.
 
<sourcesyntaxhighlight lang="python">
f=NMesh.Face()
</syntaxhighlight>
</source>
 
А потім в неї додають потрібні вершини. З об'єкта. (Щоб він міг розібратись з індексами). Десь ось так:
 
<sourcesyntaxhighlight lang="python">
f.v.append(obj.verts[0])
f.v.append(obj.verts[1])
f.v.append(obj.verts[2])
</syntaxhighlight>
</source>
 
Після того як грань буде готова додаємо її в об'єкт.
 
<sourcesyntaxhighlight lang="python">
obj.faces.append(f)
</syntaxhighlight>
</source>
 
А так як наш об'єкт - трикутник, то цього достатньо. Залишилось тільки зробити так, щоб він появився у вікні блендера.
 
<sourcesyntaxhighlight lang="python">
NMesh.PutRaw(obj,"Triangle",1)
</syntaxhighlight>
</source>
 
Першим параметром передається об'єкт, що буде вставлятись в сцену, другим - його ім'я. Якщо ім'я унікальне - створиться новий об'єкт. Якщо ні - то той об'єкт що носив це ім'я раніше заміниться на наш новий. Тому, будьте обережні. Третій параметр - прапор, що вказує блендеру, про необхідність обчислення нормалей. Нуль - ми задали нормалі. Інакше - обчислити їх.
Рядок 250:
Зразу покажу скрипт який це намалював:
 
<sourcesyntaxhighlight lang="python">
#!BPY
 
Рядок 301:
 
genheightmap(f,-5,-5,5,5,0.7)
</syntaxhighlight>
</source>
 
Він майже аналогічний скрипту що малював трикутник, з тією відмінністю, що трикутників тут набагато більше. Принцип роботи дуже простий. Подивимось на нашу карту висот в режимі дротяної моделі. Функцію я регулярно змінював, бо цікаво ж.
Рядок 313:
Тому, нам спочатку треба обчислити координати вершин на однорідному розбитті, та заповнити ними модель:
 
<sourcesyntaxhighlight lang="python">
for y in range(height):
for x in range(width):
Рядок 321:
v=NMesh.Vert(fx,fy,z)
obj.verts.append(v)
</syntaxhighlight>
</source>
 
А потім, об'єднати їх в трикутники, по два трикутники на клітинку, як на попередній ілюстрації. Зауважте, що якщо ми маємо <code>width</code> вершин в ряді, то клітинок в ряді буде <code>width-1</code>.
Рядок 330:
Графічний інтерфейс у блендері створюється ну просто дуже просто. Простіше хіба що в Delphi. За створення інтерфейсу відповідає модуль <code>Blender.Draw</code>. І одна команда:
 
<sourcesyntaxhighlight lang="python">
Blender.Draw.Register(draw,event,button)
</syntaxhighlight>
</source>
 
Ця команда приймає в параметрах три функції, які вона запускає в разі необхідності:
Рядок 345:
Його малює така функція:
 
<sourcesyntaxhighlight lang="python">
def draw():
Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT)
Рядок 357:
Blender.Draw.String("max_y: ",8,285,10,125,20,str(maxy),10,"Maximal y value",changestring)
Blender.Draw.String("d: ",9,340,50,70,20,str(resolution),10,"Size of one grid cell",changestring)
</syntaxhighlight>
</source>
 
Перший рядок функції
 
<sourcesyntaxhighlight lang="python">
Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT)
</syntaxhighlight>
</source>
 
очищає все поле для малювання. Якщо ви програмували в OpenGL - ця функція може видатись вам знайомою. А все тому, що інтерфейс блендера виводиться з допомогою OpenGL. І таке рішення видається досить вдалим.
Рядок 369:
Наступна функція
 
<sourcesyntaxhighlight lang="python">
Blender.Draw.Toggle("Create given surface",1,10,10,150,20,0)
</syntaxhighlight>
</source>
 
[[Файл:Blender cartesian.png|frame|right|Ось так]] Створює кнопку - переключатель. Перший параметр - напис на кнопці. Другий - її унікальний номер. За цим номером обробник події дізнається від якої кнопки прийшов сигнал. Тому бажано щоб кожен елемент інтерфейсу мав унікальний номер, якщо він звісно не дублює функцію якогось іншого. Два інші числа - координати кнопки в пікселях. Що цікаво, тут на відміну від інших фреймворків інтерфейсу координати математичні. Тобто нуль внизу, та ігрик направлений вверх.
Рядок 379:
Наступний оператор аналогічно створює подібну кнопку. Але ми малювали ще елемент іншого виду - для вводу тексту:
 
<sourcesyntaxhighlight lang="python">
Blender.Draw.String("f(x,y)=",3,160,50,180,20,functiontext,200,"Input surface function here",changestring)
</syntaxhighlight>
</source>
 
Перший параметр - підпис. Цей підпис буде стояти зліва від тексту що ми будемо вводити, і буде займати ширину нашого компонента. Тому упевніться що і для вводу виділено достатньо. Наступні п'ять параметрів як і у кнопки - переключателя: ідентифікатор події, координати, розмір. Після них йде рядок (<code>functiontext</code>) - у якому вказують текст що буде стояти в полі для вводу за замовчуванням. Після нього - число (200) - максимальна кількість символів для вводу (не більше 399). Потім підказка, і останнє - функція зворотнього виклику <code>changestring</code>. Ця функція викликається після введення тексту, і їй передаються два аргументи - спершу ідентифікатор події, а потім параметр.
Рядок 389:
Розглянемо спочатку обробник <code>button</code>:
 
<sourcesyntaxhighlight lang="python">
def button(evt):
if evt == 1: # Якщо натиснута кнопка з ідентифікатором 1
genheightmap(f,minx,miny,maxx,maxy,resolution,functiontext) # Створити карту висот, з заданими параметрами
Blender.Window.Redraw() # Та обновити вміст вікон блендера
</syntaxhighlight>
</source>
 
Ось так просто. Так само туди вписуємо обробку подій для інших кнопок. <code>if evt == 2</code> і так далі.
Рядок 400:
Функція - обробник спеціально для текстів нічим не складніша. Текст передається у змінну <code>val</code>. Єдиний недолік - вивід проводиться через глобальні змінні, та я не знаю як можна зробити інакше.
 
<sourcesyntaxhighlight lang="python">
def changestring(event,val):
if event==3:
global functiontext
functiontext=val
</syntaxhighlight>
</source>
 
Якщо кнопка для вводу тексту мала ідентифікатор 3, і функцію обробник - <code>changestring</code>, то з її допомогою можна було б змінювати вміст глобальної змінної <code>functiontext</code>.
Рядок 411:
Нам залишилось розглянути тільки функцію:
 
<sourcesyntaxhighlight lang="python">
def event(evt,val):
if evt == Blender.Draw.ESCKEY:
Blender.Draw.Exit()
return
</syntaxhighlight>
</source>
 
Вона майже нічого не робить, окрім того, що при натисненні клавіші Esc припиняє роботу скрипта.
Рядок 424:
Ну, і звісно спочатку варто було зареєструвати глобальні змінні, та створити функцію що малює поверхню:
 
<sourcesyntaxhighlight lang="python">
functiontext="0"
surfacename="heightmap1"
Рядок 435:
def f(x,y):
return eval(functiontext,{"x":x,"y":y,"sin":math.sin,"atan2":math.atan2})
</syntaxhighlight>
</source>
 
Увесь скрипт цілком виглядає так:
 
<sourcesyntaxhighlight lang="python">
#!BPY
 
Рядок 547:
Blender.Draw.Register(draw,event,button)
</syntaxhighlight>
</source>
 
== Посилання ==