...
...

Эффективное программирование 3D-приложений с помощью Irrlicht и Jython. Часть 5

Весь предыдущий материал подготавливал нас к этой главе. Сегодня мы попробуем применить полученные нами знания в языке python, вызывая графические функции irrlicht. Сначала займемся настройкой и установкой. Аккуратно пройдите все шаги.

1. Вы скачали с сайта sun инсталляционный пакет jdk и установили его. Предположим, что, как и у меня на компьютере, это папка
E:\Program_Files_2\j2dk1.6.0.
2. Вы скачали с сайта jython.org инсталляционный пакет jython и установили его (инсталляционный пакет — это файл с расширением .jar — исполняемый файл для java-машины), также предположим, что это была папка: E:\Program_Files_2\Jython.
3. Вы скачали с сайта irrlicht.sourceforge.net непосредственно 3d-пакет и установили в папку E:\Program_Files_2\irrlicht-1.1.
4. Вы скачали с сайта jirr.sourceforge.net биндинги для связи irrlicht и java и установили в папку E:\Program_Files_2\jirr_0.8.
5. Теперь скопируйте в папку E:\Program_Files_2\jython файлы Irrlicht.dll, irrlicht_wrap.dll, 3dx9.dll, d3dx8.dll из папки
E:\Program_Files_2\jirr_0.8\bin. Для мотивации и увеличения энтузиазма попробуйте запустить находящиеся в этой же папке файлы с расширением .bat — это примеры возможностей irrlicht+jirr.
6. Создайте копию файла E:\Program_Files_2\Jython\jython.bat. Я его назвал E:\Program_Files_2\Jython\jython_jirr.bat. Содержимое файла нужно исправить по аналогии с примером ниже:

@echo off
set ARGS=
:loop
if [%1] == [] goto end
set ARGS=%ARGS% %1
shift
goto loop
:end

"E:\Program_Files_2\j2dk1.6.0\jre1.6.0\bin\java.exe" -Dpython.home=
"E:\Program_Files_2\Jython" -classpath
"E:\Program_Files_2\Jython\jython.jar;%CLASSPATH%;
E:\Program_Files_2\jirr_0.8\lib\jirr08.jar; E:\Program_Files_2\jirr_0.8\lib\basicplayer\basicpla yer2.3.jar;E:\Program_Files_2\jirr_0.8\lib\basicplayer\commons-logging-
api.jar;E:\Program_Files_2\jirr_0.8\lib\basicplayer\jl1.0.jar;
E:\Program_Files_2\jirr_0.8\lib\basicplayer\jogg-
0.0.5.jar;E:\Program_Files_2\jirr_0.8\lib\basicplayer\jorbis-
0.0.12.jar;E:\Program_Files_2\jirr_0.8\lib\basicplayer\mp3spi1.9.2.jar;
E:\Program_Files_2\jirr_0.8\lib\basicplayer\tritonus_share.jar;E:\Program_ Files_2\
jirr_0.8\lib\basicplayer\vorbisspi1.0.jar" org.python.util.jython %ARGS%

Последняя строка начиная с "E:\Program_Files_2\j2dk1.6.0\jre1.6.0\bin\java.exe" и до конца файла — это одна большая сплошная строка без абзацев. В ней я перечислил пути к библиотекам из поставки jirr. Вообще говоря, если вы хотите в среде jython использовать средства некоторой библиотеки java, то вам необходимо добавить путь к ней в секцию –classpath. 

При первом запуске файла E:\Program_Files_2\Jython\jython_jirr.bat jython сообщит, что нашел новые библиотеки, выведя множество строк вида: "*sys-package-mgr*: processing new jar", а тут непосредственно имя файла jar — библиотеки java. Затем появится знакомое по python приглашение командной строки, но уже машины jython. Попробуйте наш пример "здравствуй мир" >>> print 'hello world from java — jirr — jython'

Как видите, ничего не поменялось. Конечно, на столь простых примерах, как этот, отличий jython от python вы не найдете, но ведь и мы в серьезные возможности python в статьях ранее не углублялись, а все то, с чем познакомились, должно работать как часы. Далее идет пример, в котором создается окно для 3d-рендеринга — в нем выводится надпись "hello world", и пока все. Важное примечание: по умолчанию java и, соответственно, jython читают данные в кодировке windows-1251. Это поведение, конечно, можно изменить, но все же после, а пока сохраняйте файлы именно в windows- 1251.

import java
import net.sf.jirr
from net.sf.jirr import dimension2di
from net.sf.jirr import recti
from net.sf.jirr import SColor

java.lang.System.loadLibrary ('irrlicht_wrap')
device = net.sf.jirr.Jirr.createDevice(
net.sf.jirr.E_DRIVER_TYPE.EDT_DIRECT3D9,
dimension2di(640, 480), 32)
device.setWindowCaption("Первая программа — irrlicht — это круто");
driver = device.getVideoDriver()
smgr = device.getSceneManager()
guienv = device.getGUIEnvironment()
guienv.addStaticText("Привет, мир, hello world", recti(10,10,260,22), 1)

while device.run():
driver.beginScene(1, 1, SColor(255,100,101,140))
smgr.drawAll()
guienv.drawAll()
driver.endScene()

device.drop

Для запуска перейдите в каталог, где у вас установлен jython, и запустите следующую команду:
E:\Program_Files_2\Jython>jython_jirr.bat "f:\kolya\programming\jirro_pojecto\src\b2.py"

Единственный аргумент, передаваемый запускаемому bat файлу jython, — это путь к файлу с исходным кодом jython, приведенным выше. Результат проявился в виде окна размером 640*480 с надписью в левом верхнем углу. Да, скажем прямо, с русскими буквами не все удалось, но это мелочи, которые мы победим позже. Теперь анализ кода. В первой строке примера я подключил модуль java, затем — модуль net.sf.jirr. Следующие команды import отличаются от ранее вам знакомых. Я подключаю следующие классы из библиотеки jirr, которые наиболее часто используются, после чего получаю возможность использовать их имена без указания предшествующего имени модуля, в котором они объявлены. Вот эти классы: dimension2di — служит для задания пар двух целых чисел, recti — задает прямоугольник с целочисленными координатами, и SColor — цвет. Многие функции irrlicht- jirr получают в качестве параметров координаты объектов, размеры, цвета. Самое главное — выполнить загрузку библиотеки irrlicht_wrap — той самой, которую вы скопировали из каталога jirr в каталог jython. Обратите внимание на то, что в имени файла нет расширения irrlicht_wrap.dll. Дело в том, что java и, следовательно, jython являются кроссплатформенными технологиями, так что написанный вами код должен заработать и под тот же linux. В нем же расширения .dll не приняты для библиотек, в отличие от windows. Поэтому вы указываете базовое имя библиотеки, а java сама добавляет нужное расширением и загружает ее.

Следующий шаг — создание устройства, куда будет выводиться графика. За это отвечает вызов функции createDevice — минимально необходимыми параметрами для нее являются указание используемого микроядра для рендеринга, в примере directx9 (под linux это уже не работает, но есть альтернативные варианты рендеринга opengl и software, когда возможности аппаратного ускорения графики не применяются). Затем необходимо указать размер создаваемого окна — он задается в виде пары целых чисел, затем идет глубина цвета — здесь 32 бита на пиксель. Затем получаем из созданного устройства ссылки на видеодрайвер, менеджер сущностей 3d-мира и менеджер gui. Последний служит для создания надписей, текстовых полей, кнопок и всего прочего, что создает интерфейс для взаимодействия пользователя с созданным 3d-миром. В примере всего лишь создалась текстовая надпись в углу с координатами 10,10 (отсчет, как уже принято, идет от левого верхнего угла) и размером 260,22. Следующий шаг — организация цикла обработки запросов, который выполняется до тех пор, пока окно не будет закрыто. В теле цикла начинается новая сцена с фоновым цветом, задаваемым с помощью SColor. Этой функции мы подали на вход 4 параметра. Первый из них — значение alpha-канала (степень прозрачности), остальные три — стандартные RGB доли цвета. После начала сцены мы отрисовали все сущности в 3d мире с помощью вызова smgr.drawAll, затем отрисовали все элементы управления с помощью guienv.drawAll(). Наконец сцена была завершена и цикл повторен заново. После окончания цикла остается только уничтожить графическое окно и освободить все захваченные ресурсы памяти, чем и занимается device.drop.

Следующий шаг — работа со средствами 2d-графики. Для примера мы нарисуем картинку "новый год пришел" с шагающим по лесу Дедом Морозом и падающими снежинками. Разумеется, мы не сможем сделать так, чтобы Дед Мороз по-настоящему замаршировал. В примере будет статическое изображение фона леса и двигающийся от левого края экрана к правому Дед Мороз. Сверху на него при этом будут падать снежинки. Также по ходу движения Деда Мороза будут размещены 4 елки. В Сети я взял первую попавшуюся картинку леса. Для нее никаких подготовительных работ не требуется. Запомните только ее размер — у меня это изображение размером 800*600. Точно такого размера нам потребуется создать 3d-устройство с помощью createDevice. Формат изображения может быть любым: bmp|jpg|tga|png. Изображения Деда Мороза, елки и снежинок также были взяты первые попавшиеся в Internet. Но важно, что перед использованием их следует подготовить. Так, фигурки должны быть размещены на прозрачном фоне. Если мы этого не сделаем, то при движении Деда Мороза вместе с ним будет перемещаться и часть фона оригинального изображения. Для того, чтобы разместить Деда Мороза на прозрачном фоне, есть целых две стратегии. Вы знаете, что существует два формата хранения изображений, поддерживающих альфа-канал: gif, png. Увы, но поддержки gif в irrlicht нет. А вот png поддерживается, в том числе и с прозрачными областями. Второй вариант основан на использовании любого непрозрачного изображения, но после его загрузки вы должны сказать irrlicht, что некоторый его цвет будет считаться прозрачным. Я для примера сделал Деда Мороза в формате jpg на черном фоне, а изображение елки — в формате png с прозрачностью. В первом случае вам ничего, кроме mspaint, не потребуется, во втором нужен более серьезный графический редактор — например, photoshop или что там вам нравится — лично я использовал PaintDotNet.

import java

import net.sf.jirr
from net.sf.jirr import dimension2di
from net.sf.jirr import position2di
from net.sf.jirr import recti
from net.sf.jirr import SColor

java.lang.System.loadLibrary ('irrlicht_wrap')
device = net.sf.jirr.Jirr.createDevice(
net.sf.jirr.E_DRIVER_TYPE.EDT_DIRECT3D9,
dimension2di(800, 600), 32)
device.setWindowCaption("Ded Moroz 1.0");
driver = device.getVideoDriver()
smgr = device.getSceneManager()
guienv = device.getGUIEnvironment()

base_dir = "f:\\kolya\\programming\\dedo_morozitto\\"
image_les = driver.getTexture(base_dir + "les.jpg");
image_ded_moroz = driver.getTexture(base_dir + "ded_moroz_black.tga");
driver.makeColorKeyTexture(image_ded_moroz, position2di(0,0));
# важно указать, какой цвет исходного изображения явлется прозрачным
image_elka = driver.getTexture(base_dir + "elka.png");
# указывать, как в прошлом случае, какой цвет является прозрачным, не надо
image_sneginka = driver.getTexture(base_dir + "sneginka.png");

time_at_start = device.getTimer().getRealTime()
# запоминаем время на начало
while device.run():
driver.beginScene(1, 1, SColor(255,100,101,140))
smgr.drawAll()
guienv.drawAll()

driver.draw2DImage(image_les, position2di(0,0) )
# рисуем фоновую картинку леса

# дед мороз должен двигаться
# поэтому его координаты — функция времени
# скажем, скорость деда мороза 1 секунда = 10 пикселей

time_now_ms = device.getTimer().getRealTime()
# теперь узнаем, сколько времени прошло с начала запуска программы в миллисекундах
delta_ms = (time_now_ms -time_at_start) / 1000.0
# теперь delta_ms хранит интервал прошедшего времени в секундах
# раз скорость движения Деда Мороза 5 пикселей в секунду, значит, весь лес
# он должен был пройти примерно за (800 + ширина_картинки_дедушки) / 10 = 90 секунд
# следовательно когда время более чем 90 секунд то дед мороз
# должен выйти снова выйти с левого края леса
while delta_ms > 90:
delta_ms = delta_ms — 90
ded_moroz_x = -96 + delta_ms*10
driver.draw2DImage(image_ded_moroz, position2di(int(ded_moroz_x),600 — 95),
recti(0,0,96,95), None,
SColor(255,255,255,255), True
)
i = 0
while i < 4:
driver.draw2DImage(image_elka, position2di(int(i * 140 + 70),600 — 111),
recti(0,0,87,111), None,
SColor(255,255,255,255), True
)# елки равномерно распределены с шагом 140
i = 1 + i # по всей ширине сцены
i = 10 # количество снежинок
while i > 0:
driver.draw2DImage(image_sneginka,
position2di(20 + i * 130, 10 + ((time_now_ms / 500 + i*19) % 20)* 35),
recti(0,0,126,138), None,
SColor(255,255,255,255), True )
i = i — 1

fps = driver.getFPS()
device.setWindowCaption("FPS: " + str(fps))

driver.endScene()
device.drop

Вначале, когда я писал данный скрипт, то сделал загрузку изображений так:
image_elka = driver.getTexture("elka.png");

При этом все файлы картинок находились в той же папке, что и скрипт (у меня он называется ded_moroz.jy). При запуске сценария таким способом: E:\Program_Files_2\Jython\jython_jirr.bat f:\kolya\programming\dedo_morozitto\ded_moroz.jy

я получил сообщение об ошибке: "irrlicht не мог найти ни одного изображения". Поэтому я ввел переменную base_dir, которая равна пути к каталогу, где находятся картинки. Это, конечно же, плохой подход: скрипт получился непереносимым — если бы вы захотели запустить его на другой машине, пришлось бы исправлять его исходный текст. Но в последующих статьях, когда мы познакомимся с модулем sys и приемами работы со строками, я покажу прием, как можно определить путь к исполняющемуся сейчас скрипту и как конструировать имя картинок динамически. Пока же объясню, почему я при записи пути использовал символы двойных слэшей. Дело в том, что в python|c|c++|java символ слэша в строке зарезервирован для пометки специальных символов — например: \n — переход на новую строку, \t — знак табулятора. Если бы я не написал двойных слэшей, то jython решил бы, что \p, \j, \j, \k, \p, \d, \d также являются специальными символами, а не первыми буквами каталогов пути к картинкам. При указании имени файла всегда следите за регистром букв. Файл snow.PNG в windows — то же самое, что и snow.png, но для linux — это не так.

Вызов функции driver.makeColorKeyTexture(image_user, position2di(0,0)) необходим для того, чтобы при отрисовке изображения driver знал, какой именно цвет используется как прозрачный. Здесь это цвет, находящийся в точке (0,0). Такой прием я использовал только для картинки Деда Мороза — его формат tga (jpg я не решился использовать из-за плохого качества). Возможен и другой вариант указания, какой цвет считать прозрачным. Так, есть функция makeColorKeyTexture, получающая вторым параметром не координаты пикселя, задающего прозрачный цвет, а объект SColor, кодирующий сам цвет. Правда, такой прием не рекомендуется применять в случае сжатых форматов, так как, возможно, тот цвет, который вы видели в своем графическом редакторе после сохранения и восстановления из, например, jpg, был заменен оттенком. Внутри главного цикла первым рисуется фон — картинка леса. Затем идет расчет координат Деда Мороза. Основная сложность — заставить его двигаться плавно. Для этого я определяю, сколько времени прошло от момента запуска программы до текущего момента. Это разница показаний, возвращаемых функцией device.getTimer().getRealTime() до начала главного цикла и каждый раз в начале его тела. Получившаяся величина хранит количество прошедших миллисекунд. Полагая, что скорость Деда Мороза 10 пикселей в секунду, я могу по школьной формуле расстояние = скорость*время вычислить координаты Деда Мороза в любой момент времени. Внимание следует обратить только на то, что по истечении 90 секунд Дед Мороз должен уйти за правую границу экрана и появиться уже слева. Для этого и служил цикл, отбрасывающий значения на 90 секунд. Для отрисовки любых картинок необходимо вызывать метод:

driver.draw2DImage(image_user, position2di(координата_x,координата_y),
recti(координата_x,координата_y, ширина_картинки, высота_картинки),
None, SColor(255,255,255,255), True )

Здесь первый параметр — непосредственно рисуемое изображение. Второй — координаты области, куда оно будет выведено. Третий задает размеры исходного изображения — так, вы можете рисовать не всего Деда Мороза, а только его часть, просто задав ширину менее 96. Четвертый параметр задает прямоугольник отсечения (в примере он не используется, поэтому вместо параметра находится специальное ключевое слово None — нет). Пятый параметр — это цвет, которым будет закрашен прямоугольник местоназначения изображения (специальное значение, когда все компоненты цвета равны 255, говорит, что никакой расцветки выполнять не следует). И, наконец, шестой — тот, ради которого были указаны все предыдущие аргументы (дело в том, что существует несколько версий функции draw2DImage, отличающихся собственно количеством параметров. Так, в простейшем случае вы могли бы указать только изображение и точку, куда его надо вывести на экран). Итак, шестой параметр — это признак того, что следует при выводе изображения учитывать его альфа-канал. Если этого не сделать, то снежинки и Дед Мороз будут на фоне черных квадратов, закрывающих фон леса. В качестве задания попробуйте заставить снежинки также плавно двигаться. Затем, когда Дед Мороз уходит за правый край экрана, он должен одновременно появляться из-за левого, так что какие-то доли секунды мы видим двух Дедов Морозов.

В следующий раз мы продолжим схему знакомства с python — в частности, мы рассмотрим понятие рекурсии, а в мире irrlicht нас ждут фракталы.

black zorro, black-zorro@tut.by

© Компьютерная газета

полезные ссылки
Аренда ноутбуков
Ремонт ноутбуков в центре Минска