Анимация элементов модели: базовые принципы

Анимация элементов модели: базовые принципы

Как уже говорилось выше, любой элемент модели, выполненный в 3D-редакторе как отдельный объект (меш) может быть анимирован в симуляторе.

Такую возможность дает сама структура файла OSGT — он представляет собой не просто геометрическую сетку с текстурными координатами, а целую сцену, в которой хранятся геометрические объекты со своей UV-разверткой, материалы, специальные эффекты на основе систем частиц. Объекты в файле OSGT позиционируются относительно центра сцены через специальные узлы трансформации — и именно воздействие на эти узлы и дает движку RRS возможность выполнять их произвольное перемещение.

RRS, на данный момент поддерживает три вида анимации:

  • анимация вращения (AnalogRotation) — обеспечивает вращение элемента относительно заданной оси на заданный угол;
  • анимация перемещения (AnalogTranslation) — обеспечивает поступательное перемещение элемента вдоль заданной оси;
  • сложная геометрическая анимация (ModelAnimation) — обеспечивает сложную процедурную анимацию группы элементов, обычно кинематически связанных между собой, предусматривает предварительную подготовку такой анимации в 3D-редакторе.

Анимации типа ModelAnimation требуют отдельного разговора, а вот первые два типа рассмотрим сейчас.

Начнем с того, что анимации реализуются на графической стороне игры. Однако многие из них, в частности вращение колес привязаны к физике движения локомотива. А мы помним, что физика и графика у нас два разных процесса. Соответственно должны быть параметры, через которые происходит передача сигналов управления анимацией в графическую часть игры. И такие параметры есть — класс Vehicle, а значит и его потомок SimpleLoco содержит массив из 200 элементов вещественного типа — analogSignal.

Предположим, мы хотим анимировать вращение колесной пар нашего локомотива. Создадим в каталоге data/animation папку с именем конфига нашего локомотива simple-loco.

Мы помним, что дали колесным парам нашего локомотива осмысленные имена: Wheel-1, Wheel-2 и так далее. Вот и в каталоге data/animations/simple-loco создаем файл с именем Wheel-1.xml

Внимание! Имя файла должно совпадать с именем анимируемого элемента модели. В файле пишем следующее содержимое

<?xml version="1.0" encoding="UTF-8"?>
<Config>
      <AnalogRoatation>
           <SignalID>194</SignalID>
           <Duration>10.0</Duration>
           <Infinity>1</Infinity>
           <Axis>-1.0 0.0 0.0</Axis>
      </AnalogRotation>

      <KeyPoint>
           <Param>0.0</Param>
           <Value>0.0</Value>
      </KeyPoint>

       <KeyPoint>
           <Param>1.0</Param>
           <Value>360.0</Value>
      </KeyPoint>
 
</Config>

Секция AnalogRoatation содержит общие параметры анимации. Да и само имя, как вы наверное догадались, задает тип анимации — вращение вокруг заданной оси. Разберем смысл каждого параметра этой секции

  • SignalID — идентификатор сигнала, то есть номер сигнала в массиве analogSignal в пределах от 0 до 199. Сигнал с указанным номером служит для управления этой анимацией;
  • Duration — скорость перемещения (вращение в данном случае) между двумя фиксированными положениями;
  • Infinity — если данный параметр равен 0, то движение будет происходить только в пределах, заданных диапазоном ключевых точек. При значении 1 анимация будет продолжаться за пределы заданного диапазона.
  • Axis — вектор, задающий направление оси вращения в системе координат модели.

Секция KeyPoint приводит в соответствие значение параметра, читаемого из сигнала с номером SignalID, с углом поворота вокруг оси, задаваемого в градусах. Таких секций должно быть как минимум две: одна задает начальную точку, другая — конечную точку перемещения. Параметры, определяемые этой секцией

  • Param — значение сигнала SignalID
  • Value — значение угла поворота, град.

В нашем случае мы определяем две ключевые точки: первая говорит о том, что значению сигнала 0 соответствует угол поворота равный 0 градусов; вторая говорит что значению параметра 1.0 соответствует угол поворота 360 градусов. То есть данные ключевые точки описывают полный оборот колеса. Если сигнал принимает значение лежащее между 0 и 1, то угол поворота вычисляется методом линейной интерполяции. Для наших колесных пар как раз и нужно такое бесконечное вращение, так их вращение не ограничивается одним оборотом. А вот, например для анимации стрелки прибора в кабине — наоборот, лучше задать ограниченную набором ключевых точек траекторию поворота.

А если сигнал превысит 1.0? Тогда, при значении Infinity равном 0 анимация прекратится — упрется в конечную точку. Если же Infinity равно 1, то анимация продолжится за счет линейной экстраполяции.

Допустим в начале сигнал имел значение 0, а потом скачком стал равен 0.5, как поведет себя вращение? Оно будет происходить равномерно, со скоростью, пропорциональной параметру Duration вплоть до достижения угла, соответствующего параметру 0.5. Чем выше Duration, тем меньше инертность анимации к изменению параметра сигнала. Однако увлекаться не стоит — для чувствительных анимаций достаточно Duration в пределах от 10 до 50. Таким образом анимация является как бы «следящей», постоянно стремясь держать модель в положении, соответствующем значению сигнала.

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

Движок игры сканирует папку с анимациями данной единицы подвижного состава, находит все конфиги и ищет в модели объекты с именем, равным имени файла конфига. Затем он приводит в соотвествие этой модели номер сигнала, считанный из конфига и начинает управлять перемещением, в соотвествии со значением сигнала с указанным номером. То есть наша задача подать этот сигнал из нашего DLL-модуля, например это можно сделать в методе step()

  void SimpleLoco::step(double t, double dt)
 {
     // Вычисляем силу тяги, которую реализует локомотив в данный момент
     double trac_force = ref_traction_level * trac_char(velocity);
 
     // Вычисляем момент, приходящийся на одну колесную пару
     double torque = trac_force * wheel_diameter / 2.0 / num_axis;
 
     // Задаем момент каждой колесной паре
     for (size_t i = 1; i < Q_a.size(); ++i)
     {
         Q_a[i] = torque;
     }
 
     // Передаем параметр поворота колесной паре
     analogSignal[194] = static_cast<float>(wheel_rotation_angle[0] / 2.0 / Physics::PI);

     DebugMsg = QString("Время: %1 с Зад. тяга: %2 Скорость: %3 км/ч Сила тяги: %4 кН")
             .arg(t, 10, 'f', 1)
             .arg(ref_traction_level, 4, 'f', 2)
             .arg(velocity * Physics::kmh, 6, 'f', 2)
             .arg(trac_force / 1000.0, 6, 'f', 1);
 }

Углы поворота колесных пар в RRS считаются движком игры в соответствии с механикой движения ПЕ. Они хранятся в массиве wheel_roattion_angle, который в случае с нашим 6-осным SimpleLoco содержит шесть элементов, нумеруемых от 0 до 5. Углы выражены в радианах. Поскольку в analogSignal передается безразмерный параметр, то есть в случае колес — число совершенных оборотов, то мы делим этот угол на 2Пи, вычисляя это самое число оборотов.

Что же, соберем DLL и проверим что у нас получилось

Ну отлично, одна колесная пара закрутилась! Чтобы это произошло и с остальными нужно создать еще пять конфигов с именами Wheel-2.xml,…,Wheel-5.xml, прописав в них сигналы от 195 до 199 и дописать код в DLL

 analogSignal[194] = static_cast<float>(wheel_rotation_angle[0] / 2.0 / Physics::PI);
 analogSignal[195] = static_cast<float>(wheel_rotation_angle[1] / 2.0 / Physics::PI);
 analogSignal[196] = static_cast<float>(wheel_rotation_angle[2] / 2.0 / Physics::PI);
 analogSignal[197] = static_cast<float>(wheel_rotation_angle[3] / 2.0 / Physics::PI);
 analogSignal[198] = static_cast<float>(wheel_rotation_angle[4] / 2.0 / Physics::PI);
 analogSignal[199] = static_cast<float>(wheel_rotation_angle[5] / 2.0 / Physics::PI);

Теперь будут двигаться все КП

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

Назад Вперед
Содержание