Программируем трехмерную графику с Irrlicht . Часть 3

August 3, 2007

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



В предыдущей статье серии мы определились с выбором 3d-движка, а также используемым языком программирования. Теперь пора приступать к работе. Начнем с того, что выкачаем из Интернета все необходимые библиотеки и программы. Все это можно разделить на две группы: то, что нужно сейчас, и то, что нам потребуется в перспективе, так что записывайте:

Нужно сейчас:
 1.	книжка Python 2.1 Bible 
 (или любая другая - посмотрите на natahaus.ru)– 7 мб.
 2.	Python 2.4 – размер примерно 25 мб.
 Нужно попозже (ко второй, может третьей статье серии):
 1.	библиотека 3d  - irrlicht-1.1.zip -  размер 11 мб. 
 Лучше пока ее не качать, может к моменту  выхода следующей статьи серии, 
 будет билд 1.2, а может и нет.
 2.	библиотека реализации jython адаптированная для совместного 
 использования с irrlicht - jirr_0_8.zip – размер 10 мб.
 3.	раз я говорил что jython – это реализация на java языка python, 
 а java – как известно тоже может быть отнесен с некоторыми оговорками 
 к семейству интерпретируемых языков, то вам потребуется интерпретатор java. 
 Идем на сайт <a href="http://sun.com">http://sun.com</a> и качаем именно jdk, не jre. 
 Размер jdk побольше, чем у jre, но это того стоит. 
 Краткая справка: jre – java runtime environment – среда исполнения кода на java, 
 предварительно преобразованного в специальный формат, оптимизированный 
 для исполнения на этой самой jre, с помощью jdk – java developer kit – тех самых утилит, 
 которым разработчик (developer) и преобразует исходный код java в исполняемый код java 
 (так называемый, magic byte code). 
 На начальных шагах мы сможем обойтись пока только jre, 
 но если серия статей будет расти и шириться, 
 то мы обязательно придем и к необходимости использовать jdk.  Размер где–то мегабайт 50.
Разумеется, что библиотеки в виде архивов распаковываются, книжка читается, а jdk и python устанавливается. Считая, что эти шаги вы выполнили успешно, перейдем далее.

С чего начинается изучение любой дисциплины – очевидно с самых простых вещей, не надейтесь, что мы тут же начнем с создания того самого 3d пинг-понга. Если пропустить введение в основы, то можно не узнать некоторые вещи которыми python и соответственно jython отличается от другого вам знакомого языка программирования. Ну а если в программировании вы, прямо скажем, ноль, то вам следует читать следующую главу от корки и до корки несколько раз.

Как известно программа – это единство двух вещей: продуманного алгоритма и эффективных структур данных. Есть даже такая книжка великого гуру Н.Вирта “Программа = алгоритм + структуры данных”.

Что такое алгоритм – очень просто, это предписание некоторому исполнителю на понятном ему языке выполнить некоторые действия. Обращайте внимание на ключевые слова: “некоторому исполнителю” – это компилятор или интерпретатор, “на понятном ему языке” – давайте определимся, раз и навсегда, что есть стандарт языка определенный в толстой книжке каким то авторитетным международным консорциумом, и вы можете делать все, что позволяется этим стандартом. Это и есть творчество программирования, но не нужно изобретать новые синтаксические конструкции, новые операторы и ключевые слова. Если вам компилятор (интерпретор) сообщает об ошибке, перечитайте, все что вы написали еще раз, сверьтесь с книжкой, вы где-то допустили ошибку.

В стандарте и его реализации - компиляторе (интерпретаторе) ошибок нет, не было и не будет, В этом в должны быть уверены также как в том что

”Ленин жил, жив и будет жить”.

Фраз с “понтами” типа “а почему он не понимает то, что я тут написал, в моем любимом basic все ему было понятно” быть не должно, раз не понимает, значит, есть ошибка. Так что RTFM, дословно, читайте этот самый, ну который, в общем, вы поняли какой, мануал.

Насчет типов данных – все тоже просто, если алгоритм что-то делает, то структуры данных хранят информацию об том, что они обрабатывают или изменяют пока это делают.

Очевидно, что хранить информацию, например, о том же кубике можно различными способами, так можно хранить координаты всех 8 вершин кубика, или же координаты центра и размер ребра, что составляет 8*3*размер_ячейки_памяти_компьютера_для_хранения_числа против (1*3+1)* размер_ячейки_памяти_компьютера_для_хранения_числа.

Одни структуры данных хранят информацию максимально компактно это – 2-ой способ, или же максимально удобно для обработки – 1-ый способ. Искусство выбрать компромисс между скоростью и размером – это тоже часть труда настоящего программиста. Одного нужно избегать всегда – дубляжа хранящейся информации. Если вы записываете, скажем, телефон вашей девушки на обоях всех стен вашей квартиры, это, конечно, удобно для быстрого поиска.

Но, если у нее поменялся номер телефона, то боюсь, вам придется пройтись по всем комнатам и исправить телефон на верный. При этом конечно вы, конечно, забудете зайти в санузел, чтобы исправить номер, написанный под ободком унитаза.

В общем, централизация и координация, чем меньше хаоса у вас в голове – тем проще и проще на более поздних стадиях программирования. Как известно, ошибка, выявленная на стадии проектирования ракеты стоит 1 рубль, а на стадии перед ее запуском - много, очень много. Не делайте эту ошибку, а то ваш первый белорусский спутник так никуда никогда не улетит.

Заходим в папку, куда вы установили python, и запускаем файл python.exe, или если у вас создался ярлычок его запуска на рабочем столе, то стартуем его. На экране появляется черное окно, в котором мы будем писать наш скрипт, пока вы видите строку-приглашение интерпретатора.
 Python 2.4.2 (#67, Sep 28 2005, 12:41:11) [MSC v.1310 32 bit (Intel)] on win32
 Type "help", "copyright", "credits" or "license" for more information.
 >>>
Вот после этих >>> и начинаем писать наше приложение. Начнем мы, пожалуй, с классического “hello world”. Программа скажет нам “здравствуй мир”, и перед нами откроется новый мир. Обращайте внимание, все, что в примерах кода написано после приглашения >>>, пишем мы, а то, что пишется с новой строки без символа приглашения ввода команды, то соответственно вернула нам машина в ответ на предыдущую команду. Команды завершаются вводом.
  1. >>> print "hello world";
  2. hello world
Обратите внимание, что есть команда, print, после которой через пробел вы пишите, что именно должна вам сообщить (напечатать на экране) машина.

Фраза hello world - это текст, раз текст то всегда мы пишем его в кавычках (одинарных или двойных), и это закон. Соответственно, если вы хотите вывести на экран значение, скажем, 7+1, то пишите формулу суммы без кавычек. Команда завершается точкой с запятой, вообще то можете не писать, это дело привычки, главное четко понимать, что этот символ играет роль, разделителя введенных команд. Если на одной строке (до нажатия ввод) у вас только одна команда, то и разделять их с помощью точки с запятой не нужно.

Если ошибок не было, то программа выполнится и выведет фразу “hello world”. Если ошибки были, то увидите нечто вроде (здесь я забыл закрыть одинарную кавычку для строки):
  1. >>> print 'hello world;
  2.   File "<stdin>", line 1
  3.     print 'hello world;
  4.                       ^
  5. SyntaxError: EOL while scanning single-quoted string
Момент еще в том, что способ работы, когда вы непосредственно в консоли набираете команды и получаете ответ от машины питона, может быть не удобен для начинающих, и соответственно делающих опечатки и прочие глупые ошибки набора.

Возможен и второй вариант, вы открываете блокнот или ваш другой любимый текстовой редактор (только ни каких продуктов а-ля word), и пишите код в них. Файл нужно сохранить с расширением “py”, если вы установили python, то он зарегистрировал данное расширение на себя. Так что по двойному клику файл со скриптом будет запущен.

Попробуйте так сделать с нашей первой программой “hello world”. Что получилось? На экране возникло черное окно, в нем появилась строка “hello world” и тут же пропало. Для того чтобы окно сразу же не исчезало необходимо указать python чтобы он ждал нажатия ввода, например, так:
  1. print 'hello' 
  2. raw_input ("press any key to close application")
В Windows программы на python имеют расширение.py или .pyw. Расширение .pyw используется для программ, использующих средства графического интерфейса.

Если возможности блокнота вас не устраивают, то на сайте python.org есть страница со списком сред разработки: http://wiki.python.org/moin/IntegratedDevelopmentEnvironments. При выборе среды разработки обращайте внимание на следующие ключевые моменты:
 1. Подсветка синтаксиса
 2. Автоматическая простановка отступов для вложенных операторов.
 3. Поддержка кодировки utf8. Исходный код программы на python
 должен быть сохранен в файле с указанной кодировкой. 
Здесь уточнение: дело в том, что до того момента, пока вы не будете пытаться в программе на python выводить строки текста на русском языке или писать комментарии на русском – все будет хорошо. Программы будут запускаться и работать, дело в том, что в кодировке utf8 латинские буквы и цифры кодируются также как в ansi или oem кодировке.

А вот символы национальных алфавитов – по-другому. В худшем случае используйте стандартный блокнот windows, который умеет сохранять файлы в кодировках ANSI, Unicode, UTF-8. Как вариант в начале файла с исходным текстом написать нечто вроде
  1. # -*- coding: cp866 -*-
Здесь вы указываете используемую кодировку файла. Именно в 866 кодировке работает консоль cmd (windows/dos),

если же вы будете указывать другую кодировку, то выводимые на экран надписи на русском языке будут не читаемы.
 4. Наличие отладчика
 5. Опционально наличие интегрированной справки, редактора графического интерфейса.
 В примерах далее я буду приводить примеры исходного текста, 
 который создал и в некотором текстовом редакторе, 
 так и непосредственно в командной строке машины python. 
 Отличайте их по наличию во втором случае приглашения для ввода команды >>>. 
 Исходный текст от способа набора, разумеется, не изменяется.
Следующий шаг, немного арифметики, посчитаем простое выражение вида:
  1+2*(3/6+7) и  1+2*(3/7+7). 
В любом случае, как вы видите в примере ниже, у нас получилось одно и тоже значение, чего быть по идее не должно.
  1. >>> print 1+2*(3/6+7);
  2. 15
  3. >>> print 1+2*(3/7+7);
  4. 15
Но вы же помните, как я говорил, что ошибок в python быть не может по определению. Значит такое поведение – это не баг, а фича – т.е. такая специфическая, немного непривычная, но имеющая свое обоснование особенность работы python.

Мы знакомимся с понятием типов данных. Каждый раз, когда вы пишите какое-то математическое выражение для всех участвующих в формуле чисел, машиной Python определяется, то какой у них тип данных. В примере выше все числа были восприняты как целые – и действительно, ведь у них же не было части после запятой, и результат при вычислении также был воспринят как целое число.

Теперь же смотрите на пример еще раз. Снова в первом случае получилось 15, но уже оно записано как вещественное число, с частью после запятой. Если же указать в формуле дробную часть (пусть даже и нулевую) именно в той части выражения, которая порождает дробную часть, то она будет сохранена. В первом случае 3/7 было высчитано в целых числах и только при добавлении к 7.0 было преобразовано к вещественным, когда уже остаток от деления был уже утерян.
  1. >>> print 1+2*(3/7+7.0);
  2. 15.0
  3. >>> print 1+2*(3/7.0+7);
  4. 15.8571428571
Обычно расчет достаточно сложен и не может быть записан в виде одной формулы, так как хотя машине все равно, и она может вычислить любое корректное выражение без ошибок, но человек может ошибиться при его записи. Следовательно, трехэтажные дроби и им подобное лучше разбивать на части, сохраняя промежуточные результаты в некоторых переменных. Затем эти промежуточные переменные используются в следующих формулах и порождают следующие переменные, в общем, получается настоящий конвейер обработки чисел.
  1. >>> count_of_month = 12;
  2. >>> count_of_days = 30;
  3. >>> days_in_year = count_of_month * count_of_days;
  4. >>> print days_in_year;
  5. 360
Вот пример трех шагового расчета количества дней в году, разумеется, он не совсем верен, т.к. не учитывает, что некоторые месяца имеют не 30, а 31, 28 или 29 дней. Так что нам нужно вводить новый прием позволяющий проверять некоторые условия и пускать этот конвейер обработки чисел по разным маршрутам. Для примера проведем пример расчета количества дней в феврале в зависимости от номера года. Мы знаем, что каждый четвертый год – високосный. Стандартный алгоритм предполагает проверку того, кратен ли данный год четырем (читай, делится ли год без остатка на 4) и если да то дней 29, иначе - 28.

Важно во всех примерах регистр букв переменных, ключевых слов – важен. Естественно при написании данного материала возможны опечатки, при наборе и корректуре с оформлением также ошибок не избежать, так что если у меня в примере переменная называется то count_of_days, а то Count_Of_Days то выберите любой вариант написания, какой вам больше нравится и придерживайтесь его всегда.

Общее замечание к правилам именования переменных, на эту тему есть “настоящий программерский” анекдот:
 Студент сдает лабораторную работу, показывает ее преподавателю. 
 Тот кивает головой, мол, все хорошо,  все нравится, только что 
 это вот батенька у вас переменные имеют непонятные имена, вот скажем a,b,c. 
 Дайте им понятные длинные и мнемонические названия. 
 Через какое то время студент приходит заново. 
 Смотрит преподаватель и видит нечто вроде:
  длинное_мнемоничное_название_переменной_#_1 = 3.14159
  длинное_мнемоничное_название_переменной_#_2 = 2+5
  и т.д.
Дело не в том, что студент объявил переменные на русском языке да еще со знаком # в названии, что уж явно не по правилам пролога, а в его отношении к работе. Непонятные и нечитаемые имена – верный способ, чтобы ваши партнеры в сердцах сказали бы: “да ну его с этой программой, здесь ничего нельзя понять, даже имена переменных ничего не говорят, нет времени разбираться”.
  1. >>> cur_year = 2006;
  2. >>> if (cur_year % 4 == 0):
  3. ...     febrary_days = 29;
  4. ... else:
  5. ...     febrary_days = 28;
  6. ...
  7. >>> print febrary_days;
  8. 28
Итак, первым шагом после присвоения переменной cur_year значения номера года 2006, идет условный оператор if (правда ведь, во всех языках условный оператор называется if). Обращайте внимание на символ двоеточия после условия, затем идет сообщение, что команда еще не завершена, и мы продолжаем ее вводить (в примерах ранее сразу после ввода команда выполнялась), здесь это сигнализируется с помощью троеточия. Завершается ввод такой сложной команды вводом пустой строки.

Важно, что раз оператор присвоения переменной febrary_days = 29 вложен внутрь условного оператора if, то его необходимо вводить с отступом, использовать табулятор или некоторое количество пробелов не важно – но отступ должен быть. На листинге это плохо видно, но отступ есть, причем для примера давайте еще:
  1. >>> if(1==1):
  2. ...  print 'a'
  3. ...  print 'b'
  4. ... else:
  5. ...  print 'c'
  6. ...  print 'd'
  7. ...
  8. a
  9. b
Еще важно, когда у вас внутрь некоторого оператора вложены несколько дочерних операторов, то пишите их с одинаковым отступом, так и перед print ‘a’ и перед print ‘b’ у меня ровно один пробел. Так когда я пытался вводить разное количество пробелов, то была ошибка. Ну как, непривычно? Для тех, кто знаком с каким то другим языком программирования, прослеживается аналогия.

Да, нужно, если есть несколько операторов, то их группировать, но для группировки используются не ключевые слова begin и end как в pascal/Delphi или фигурные скобки {} как с/с++/c#/java, а именно отступы вложенных операторов. Да это непривычно, и для знакомых с другими языками, приходится ломать стереотипы, но учит писать код аккуратно и удобочитаемо. Если вы привыкли писать код, не используя отступы перед операторами, для того, чтобы они выглядели елочкой, значит, я бы вас на работу не принял, как и любой другой грамотный работодатель.

Так как важной частью профессии программиста является культура коллективной разработки, и вы должны писать такой код и так, чтобы ваш сосед мог его взять и читать как, скажем например, стихи, конечно, стих можно записать и в одну строку, но будет ли это стихом, вопрос уже к филологам.

В следующей статье серии мы все еще будем продолжать тренироваться с python. Нам нужно будет рассмотреть как организовывать в нем циклы, как хранить сложные структуры данных. Также познакомимся с некоторыми из стандартных библиотек и функций python.