Введение в моделирование для авиасимулятора FlightGear. Часть 2

Материал из FlightGear.ru.

Перейти к: навигация, поиск

Продолжение статьи. См. начало в Введение в моделирование для авиасимулятора FlightGear. Часть 1

Содержание

Виртуальный кокпит

Если есть визуальная модель - значит, есть и виртуальный кокпит. Давайте туда переместимся.

Откроем наш редактор, и выберем координаты точки, где бы мы хотели поместить точку наблюдения в ВК - другими словами, откуда пилот будет смотреть на окружающий мир. Я взял точку с координатами X:-4.3, Y:-0.5, Z: 2.6. Точка наблюдения смещена влево на полметра от продольной оси модели - считаем, что там у нас "левая чашка":)

Конфигурируем обзор. В файл tes-set.xml, в секцию /sim добавляем:

   <virtual-cockpit archive="y">true</virtual-cockpit>
   <view>
     <internal archive="y">true</internal>
     <config>
       <x-offset-m archive="y">-0.5</x-offset-m> 
       <y-offset-m archive="y">2.6</y-offset-m> 
       <z-offset-m archive="y">-4.3</z-offset-m> 
     </config>
 </view>

Мы сконфигурировали нулевой режим обзора. Режимов обзора по умолчанию шесть: Cockpit View, Helicopter View, Chase View, Tower View, Tower View Look From, Chase View wo yaw. Переключаеются виды клавишей V, но очень желательно сдублировать ее на джойстике.

Вы можете добавлять свои собственные обзоры и менять дефолтные установки существующим. Кстати, все дефолтные настройки симулятора живут в файле preferences.xml.

Нулевой режим обзора - ВК (Cockpit View). Сим стартует именно в нем, но если он не сконфигурирован (как было у нас раньше) - в этом обзоре модель будет не видна - только окружающий мир.

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

Теперь, когда вы запустите сим, вы можете повертеть головой внутри ВК нашей модели. Конечно, там сейчас пусто, и для реальных моделей обязательно нужно построить интерьер (3D, обращая внимание на нормали!), расставить пилотские кресла, воздвигнуть штурвальные колонки (анимированные, а как же!), но для освоения принципов пока пусть все будет как есть. Главное - мы видим перед собой панель, и все готово для того, чтобы расставить на ней приборы.

Приборы

Приборы - это то, что в MSFS называются "Gauges", а в FlightGear - Instruments. Не знаю, почему в FG используется другое слово. Возможно, чтобы избежать конфликтов с Сами Знаете Какой фирмой, любящей патентовать разные общеупотребительные слова в виде торговых марок своих изделий.

Дефолтных 3D приборов довольно много - они живут в директории FlightGear/Aircraft/Instruments-3d/. Начнем с asi300, который, как можно догадаться, измеряет воздушную скорость до 300 узлов.

Приборы, с точки зрения сима - это отдельные, дополнительные модели, со своими конфигурационными xml-файлами. Вся анимация прибора живет именно в них, поэтому добавление готового и отлаженного прибора в модель сводится к позиционированию его в системе координат модели. Основная трудность здесь - это определение точного места прибора на панели. Какая-либо автоматизация здесь отсутствует, поэтому подгонка положения приборов на панели - процесс итерационный и медленный, т.к. модель собирается один раз при старте сима, и чтобы обновить изменения в конфигурационных файлах, приходится перезапускать сим. Впрочем, навык приходит быстро - важно сразу как можно точнее измерять желаемое положение прибора в графическом редакторе.

Запускаем редактор, определяем положение прибора (его центра) на панели. Для удобства,можно скопировать модель в другой файл и в нем часть объектов модели прибить, оставив только панель - ни в коем случае не сдвигая ее!

Устанавливаем прибор - добавляем в test-model.xml:

<model>
   <name>asi</name>
   <path>Aircraft/Instruments-3d/asi300/asi300-3d.xml</path>
   <offsets>
     <x-m>-6.955</x-m>
     <y-m>-1.0</y-m>
     <z-m>1.5</z-m>
   </offsets>
</model>

Здесь мы указываем путь к xml-файлу прибора, и устанавливаем его смещение в координатах модели. При необходимости, прибор можно повернуть, наклонить - если у нас панель не вертикальна - см. синтаксис тега <offsets> в документации. Теперь можно запустить сим и убедиться, что прибор на месте. Для этой панели он явно мал (точнее, велико расстояние от точки наблюдения), это легко исправить. Измените координаты точки наблюдения ВК - X:-6.3 Y:-0.8 Z: 1.7, помня о перепутанных осях.

Видно, что прибор работает, и что скорость у НЛО значительно выше 300 knots.

Давайте откроем Aircraft/Instruments-3d/asi300/asi300.ac и посмотрим, как устроен этот прибор изнутри. Он состоит из трех объектов: циферблата ASIface, стрелки ASIneedle и наружного кольца с головками заклепок ASIbezel. Первые два объекта текстурированы - смотрите файл Aircraft/Instruments-3d/asi300/asi-300.rgb. Таким примерно способом устроены все приборы типа "будильник". Обратите внимание на следующие вещи:

Нормали объектов должны быть правильными - иначе объекты будут либо неправильно подсвечены, либо вообще исчезнут под определенным углом наблюдения.

Прибор ориентирован так, чтобы ось X "выходила" из него, и была направлена к наблюдателю. При этом, когда вы будете устанавливать его на панель, вам не придется применять дополнительные теги, разворачивающие прибор "лицом" к наблюдателю.

Геометрический центр прибора совпадает с нулем координат, одна из осей стрелок совпадает с осью X. В этом случае, предельно упрощается анимация стрелки - по умолчанию, ось вращения проходит через начало координат и совпадает с осью X.

Давайте добавим более сложный прибор, указатель скорости PA24. Добавляем следующие строки в test-model.xml:

<model>
 <name>asi-pa24</name>
 <path>Aircraft/Instruments-3d/asi-pa24/asi.xml</path>
 <offsets>
   <x-m>-6.94</x-m>
   <y-m>-0.8</y-m>
   <z-m>1.5</z-m>
 </offsets>
</model>

Я слегка изменил координату по X, т.к. панель кривовата и прибор "проваливается под панель". Как видим, при известных координатах, делать панели из готовых блоков несложно.

Теперь немного беллетристики. На мой взгляд, основным секретом изготовления интересных, выразительных приборов, являются качественные текстуры. Сами по себе, объекты приборов очень просты, и в большинстве случаев даже не трехмерны. Иллюзию объема придает качественно сделанные текстуры. При этом, к текстурам применимы такие же требования, как и текстурам MSFS - т.е. размеры в пикселах должны быть степенью двойки (128х128, 256х16 итд), с определенным предельным соотношением сторон. В документации встречается число 1/8, но я, помнится, успешно делал текстуры и с бОльшим отношением.

Формат SGI RGB, в котором FlightGear хранит текстуры, поддерживает альфа-канал, что позволяет создавать очень интересные объекты при минимуме полигонов. С успехом можно делать приборы на основе фотографий, для модели Ан-2 я сделал несколько таких приборов, взяв фото отсюда. Правда, чтобы это все выглядело достойно, нужен определенный навык при работе с двумерными графическими редакторами - в первую очередь, как ни странно, векторными. Специалисты-дизайнеры, думаю, со мной согласятся, но это уже за пределами данной статьи...

Однако тут есть одна тонкость, непонимание которой может сильно осложнить жизнь, и про которую я упоминал в разделе "анимация". Если у нас есть перекрывающиеся текстурированные объекты, для правильного их отображения необходимо, чтобы порядок следования таких объектов в исходном файле трехмерной модели был строго определен. А именно: текстурированные объекты должны загружаться в сим в том порядке, в котором они стоят в сцене. Это относится не только к приборам, но также и к визуальной модели в целом.

Допустим, наш прибор состоит из трех объектов: циферблат, стрелка и крышка - прозрачное стекло с ободком . Когда мы смотрим на прибор, сначала мы видим крышку, под ним, через стекло - стрелку, под ней - циферблат. Чтобы сим правильно отрисовал такой прибор, необходимо, чтобы порядок следования объектов в файле был: вначале - циферблат, потом - стрелка, и последней - крышка. Понятно, что это требование не так просто выполнить: хотя редакторы имеют возможность реорганизовывать объекты, это неудобно, и часто, в случае сложной модели, гораздо проще переставить объекты в ac файле вручную, с помощью обычного текстового редактора. Именно поэтому, текстовые форматы для трехмерных моделей для FG предпочтительнее.

Существует еще один способ принудительно указать порядок следования объектов: воспользоваться анимацией <none>. Эта анимация перегруппировывает объекты в тот порядок, который нужен для их правильного отображения. Кроме этого, анимация <none>, если указать ей имя, позволяет впоследствии по этому имени анимировать группу объектов как одно целое. С другой стороны, любая анимация, где объединены несколько объектов (т.е. где несколько тегов <object-name>), производит перегруппировку этих объектов. Но, поскольку правила группировки не допускают, чтобы один объект входил одновременно в несколько групп, существует определенная процедура, которая выполняется, когда мы пытаемся собрать в новую группу объект, уже состоящий в другой группе. Все это требует внимания, и на первом этапе освоения лучше вообще избегать много-объектной анимации, потому что ошибки группировки приводят к исчезновению объектов, неправильной работе других анимаций. Подобные ошибки "на ровном месте" могут сильно испортить впечатление от процесса моделирования, поэтому одно из правил начинающего моделлера должно быть - "вначале - никаких групп!". После отладки анимаций, можно пытаться сгруппировать однотипные анимации, но при этом важно внимательно проверять результат.

Давайте рассмотрим особенности приборной анимации. В первую очередь, это интерполяция. Вот пример анимации стрелки индикатора воздушной скорости из asi-pa24/asi.xml:

<animation>
 <type>rotate</type>
 <object-name>Needle</object-name>
 <property>/instrumentation/airspeed-indicator/indicated-speed-kt</property>
 <interpolation>
  <entry><ind>  10</ind><dep>    3</dep></entry>
  <entry><ind>  40</ind><dep>   35</dep></entry>
  <entry><ind>  60</ind><dep>   70</dep></entry>
  <entry><ind>  80</ind><dep>  105</dep></entry>
  <entry><ind> 100</ind><dep>  155</dep></entry>
  <entry><ind> 120</ind><dep>  190</dep></entry>
  <entry><ind> 140</ind><dep>  225</dep></entry>
  <entry><ind> 160</ind><dep>  260</dep></entry>
  <entry><ind> 180</ind><dep>  295</dep></entry>
 </interpolation>
 <center>
  <x-m>0.0</x-m>
  <y-m>0.001</y-m>
  <z-m>0.001</z-m>
 </center>
 <axis>
  <x>-1</x>
  <y>0</y>
  <z>0</z>
 </axis>
</animation>

Обратите внимание: <axis> задает вектор в направлении "минус X". Это типично для приборов - положительные углы в этом случае будут приводить к вращению объекта по часовой стрелке.

Кроме известных уже нам тегов <type> <object-name> <property> <center> <axis> здесь используется тег <interpolation>. Он задает таблицу интерполяции, состоящую из строк (теги <entry>). В каждой строке есть два элемента: <ind> и <dep>. Первый из них, <ind>, задает входное значение интерполяции, второй <dep> - выходное. Входные значения берутся из <property>, таким образом, можно считать, что <interpolation> - это нелинейный коэффициент, на который умножается величина, взятая из <property>, и в результате получается угол, на который будет повернут объект. Из приведенной таблицы видно, что на скорости 10 узлов стрелка будет повернута на 3 градуса, а на скорости 100 узлов - на 155 градусов (<ind> в данном примере - узлы, <dep> - градусы).

Важное замечание по интерполяции. Первый и последний элементы таблицы определяют границы анимации, т.е. в данном примере при сколь угодно большой скорости стрелка никогда не повернется больше чем на 295 градусов, и даже на стоянке стрелка будет повернута на 3 градуса. Таким образом, интерполяция одновременно обрезает диапазон анимации, заменяя собой теги <min>, <max>.

Еще одно важное замечание. Интерполяция работает только для монотонно изменяющихся функций.

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

Откуда приборы получают данные? В симуляторе есть довольно много готовых приборов - они находятся в ветке /instrumentation. Например, скоростемеры, которые мы включали в свою тестовую модель, пользуются данными из /instrumentation/airspeed-indicator/. Запустите сим, откройте браузер свойств, зайдите в эту ветку и посмотрите, сколько инструментария создано для поддержки авторов моделей. Кстати, гироскопы моделируются с учетом дрейфа на данной широте, и даже моделируется выбег гироскопа после пропадания питания - поиграйтесь с параметром serviceable. Для некоторых приборов есть арретир - caged-flag. А если вам требуется несколько независимых экземпляров одного прибора - например, несколько радиокомпасов, каждый на свою частоту, или три гировертикали - можно задать их требуемое количество в файле instrumentation.xml в директории модели - взяв за основу FlightGear/Aircraft/Generic/generic-instrumentation.xml.

NASAL

Как быть, если нужного прибора нет в /instrumentation? как смоделировать сложную самолетную систему - электрическую, гидравлическую? топливную систему магистрального лайнера? каким образом создавать анимации, для которых требуются переменные, меняющиеся по сложным законам?

Ответ один. Запрограммировать все это с помощью встроенного языка программирования nasal.

Nasal - полноценный язык программирования, по своему синтаксису похожий на Си, а по идеологии - нечто близкое к перлу и питону. По крайней мере, авторы языка ведут сравнение nasal именно с этими языками. Nasal - интерпретатор, при старте программы ее текст транслируется в байткод и затем выполняется довольно шустро. Вряд ли у меня получится рассказать о языке так же подробно и точно, как тут:

Зато я покажу, как пользоваться некоторыми возможностями встроенного языка программирования.

Запустить программу (скрипт) nasal несложно. Создаем файл myprog.nas в директории test, где находится наша модель. Пишем туда:

# my first program
hello = func{
print("script work!");
}
hello();

Подключаем нашу программу к модели. Добавляем в test-set.xml следующие строки:

<nasal>
 <test>
   <file>Aircraft/test/myprog.nas</file>
 </test>
</nasal>

Запускаем сим и в консоли, в окне, куда выводятся сообщения, наблюдаем ожидаемый текст.

Пояснения к программе. Мы описали функцию hello(), которая выводит сообщение в консоль. А потом, мы эту функцию вызвали один раз, при старте симулятора.

Казалось бы, бессмысленная программа. давайте чучуть усложним.

# my 2-nd program
hello = func{
print("script work!");
settimer( hello, 0.5 );
}
hello();

Теперь, после вызова в первый раз, функция hello() устанавливает таймер, куда передает свое имя и период времени. По истечении тайм-аута, таймер вызовет функцию вновь, и процесс повторится.

Это один из основных способов применения nasal. В симуляторе часто необходимо выполнять какие-то действия периодически, и для этого служит встроенная функция settimer(). Второй параметр функции - период срабатывания в секундах. Если вторым параметром функции поставить ноль, то таймер будет срабатывать с частотой главного цикла симулятора.

Другое применение nasal - обработка внешних событий (нажатий на клавиши, перемещение джойстика етс).

добавьте в myprog.nas

key_handler = func{
print("Key pressed!");
}

а в test-set.xml измените связывание для клавиши g, чтобы тег <key> выглядел так:

     <key n="103">
       <name>g</name>
       <binding>
         <command>nasal</command>
         <script>test.key_handler()</script>
       </binding>
     </key>

Работает? давайте разберемся с параметрами. Вот так в функцию можно передавать параметры:

key_handler = func{
if( arg[0] == 1 )
 {
print("Key pressed!");
 }
if( arg[0] == -1 )
 {
print("Shift+Key pressed!");
 }
}

Тогда можно несколько клавиш связать с одной функцией (сравните с предыдущим примером):

     <key n="103">
       <name>g</name>
       <binding>
         <command>nasal</command>
         <script>test.key_handler(1)</script>
       </binding>
     </key>
     <key n="71">
       <name>G</name>
       <binding>
           <command>nasal</command>
           <script>test.key_handler(-1)</script>
       </binding>
     </key>   

Ввод-вывод в Nasal предназначен в первую очередь для работы с property tree. существуют довольно продвинутые средства работы с группами переменных (узлами, Node), но для начала давайте посмотрим как работать с одной переменной:

key_handler = func{
 gear = getprop("/tuxmodel/gear");
 if( gear == nil ) { return; }
 print("gear up:", gear);
 if( arg[0] == 1 )
 {
 print("Key pressed!");
 }
 if( arg[0] == -1 )
 {
 print("Shift+Key pressed!");
 }
}

Тут мы перед проверкой аргумента функции читаем переменную из property tree (ранее нами инициализированную), и присваиваем локальной переменной gear. Затем, выполняется проверка на nil - неопределенное свойство. Это необходимо, т.к. разные компоненты симулятора при старте инициализируются в разное время, и когда интерпретатор nasal уже готов к работе, property tree еще обрабатывается. Если в этот момент будет предпринята попытка использовать переменнную в числовом контексте (для арифметики, например) - будет зафиксирована ошибка, и nasal выгрузит целиком весь файл, дальнейшая работа функций из всего файла будет невозможна даже после полной инициализации property tree.

Давайте запишем свойство. Еще раз модифицируем программу:

key_handler = func{
 if( arg[0] == 1 )
 {
  setprop("/tuxmodel/gear", 0.0);
 }
 if( arg[0] == -1 )
 {
  setprop("/tuxmodel/gear", 1.0);
 }
}

Думаю, тут все понятно. А теперь - демонстрация работы еще одной очень полезной функции:

key_handler = func{
 if( arg[0] == 1 )
 {
  interpolate("/tuxmodel/gear", 0.0, 2.0 );
 }
 if( arg[0] == -1 )
 {
  interpolate("/tuxmodel/gear", 1.0, 2.0 );
 }
}

теперь "шасси" выпускаются плавно, а не конвульсивно дергаются.

Interpolate() изменяет величину переменной (первый параметр) в течении заданного интервала времени (третий параметр) до заданной величины (второй параметр функции). Смысл в том, чтобы дать команду симулятору плавно изменять переменную до требуемой величины, вместо того чтобы изменить ее скачком. Это отличный способ задавать плавно меняющиеся движения, если переменная связана с анимацией объектов. Получаются выразительные анимации ручек, реалистичные лампочки - те мелочи, которые определяют в конце концов общий уровень модели.

Вот, мы освоили основные применения nasal в FlightGear. Теперь в вашем распоряжении полноценный процедурный язык программирования, средства ввода-вывода через property tree и отладочный вывод. Вы можете выполнять довольно сложную математическую обработку любого параметра FlightGear, а так же строить программные автоматы для моделирования систем. В директории FlightGear/Nasal находятся файлы с различными дополнениями, удобными и полезными функциями и всякими фичами - все они доступны из кода ваших моделей. При этом, скрипты работают довольно эффективно, и даже большое их количество сим не притормаживает. Можно читать файлы (писать нельзя - из соображений безопасности). Можно выводить красивые надписи и даже пользоваться готовыми виджетами для разных таблиц, менюшек итд.

Вообще, nasal, интегрированный в FlightGear, очень мощная вещь. Полет вашей творческой фантазии практически ничем не ограничен.

Заключение

Здесь я просто перечислю темы, которые остались за рамками статьи. Во-первых, и это, наверно,самая важная подсистема симулятора - полетная динамика. Обязательно загляните сюда. Вообще, создание реалистичной динамики - дело творческое и требующее определенных познаний в теории, и опусы типа "полетная динамика для чайников за четверть часа" вряд ли получатся. В помощь начинающим на сайте JSBSim есть раздел Aeromatic, который поможет создать упрощенную динамику для самолета и винтомоторной группы. Есть от чего оттолкнуться, и дальше уже можно двигаться самостоятельно.

Я старался, чтобы статья дала возможность самостоятельного изучения существующих моделей - разного качества, разных авторов, и учиться приемам использования различных анимаций и программирования nasal. Это отличный способ разобраться во всех тонкостях моделирования FlightGear и большое преимущество открытых систем. Используйте эту возможность как можно чаще!

В статью не вошли описания озвучки, субмоделей, трафика и сетевых возможностей симулятора. Программирование этих подсистем уже требует определенного опыта работы с FlightGear. И, конечно, задача, требующая отдельной статьи, и не одной! - создание сценариев...

Другими словами, для авторов моделей есть все возможности, чтобы приложить силы, проявить талант и достичь успеха. Будут модели - будет расти интерес к симулятору, ну а пока он почти неизвестен в нашей стране. Чтобы слегка сдвинуть дело с мертвой точки, под FlightGear портирована хорошо известная модель Ан-2 Антона Николаева. Виртуальный кокпит модели неплохо иллюстрирует возможности технологий FlightGear.

Добро пожаловать на http://www.flightgear.ru !

Ю.В,Никифоров aka Yurik_nsk

yurik{ухЪ}megasignal.com

май 2007

Личные инструменты