Добавляем плавную анимацию.
главная страница статьи файлы о сайте ссылки
Добавляем плавную анимацию.

Файлы к заметке:
Здесь реализована плавная анимация БЕЗ кватернионов:
исходники плавной анимации: smooth-src.rar (58kb)
исходники вместе с откомпилированным exe файлом: smooth-exe (227kb)

Обратите внимание - самая новая версия загрузчика моделей Half-Life smd: текущая версия загрузчика SMD.

С кватернионами пока что есть только "продвинутая" версия для tmt pascal, в которой я очень намудрил с указателями (pointers) - в разделе файлы.

Ссылки по теме:
Этот же пример без плавной анимации (для простоты).
Исправление глюков - модель больше не "выворачивает"! - полезно

Георгий Мошкин
tmtlib@narod.ru

Сначала про плавность без кватернионов.

Вот почему сейчас почему рывками всё идёт? Потому что мы рывками меняем номер кадра:

inc(kadr);

и как только kadr увеличивается на еденицу, то сразу же берутся значения из следующего кадра.

И так доходим до последнего кадра и счётчик опять на нулевой кадр идёт:

if kadr>length(anim.kadri)-1 then kadr:=0;

Плавность делается таким образом: нужно "сгенерировать" дополнительные кадры вручную. Для этого нужно знать номер текущего кадра и номер следующего кадра. Текущий номер уже есть (kadr). А следующий:

sledKadr:=kadr+1;

Если всего 9 кадров (нумерация от 0 до 8):

(kadr=0 sledKadr=1)
(kadr=1 sledKadr=2)
(kadr=2 sledKadr=3)
(kadr=3 ..........)
(.................)
(.......sledKadr=8)
(kadr=8 sledKadr=0) <- здесь sledKadr НЕ РАВЕН kadr+1;

Дальше мы добавляем время между кадрами. Лучше сделать время от 0 до 1. То есть у нас есть:

timeKadr:single; // число с точкой
kadr:integer; // целое число
sledKadr:integer; // целое число

Теперь мы заменяем

inc(kadr);

на

timeKadr:=timeKadr+0.01;

if timeKadr>=1 then
begin
timeKadr:=0;
inc(kadr);
end;

Вместо 0.001 лучше ставить число в зависимости от производительности компьютера (делить FPS на 1000 или что-то в этом роде).

Осталось лишь сгенерировать новые значения поворотов и перемещений. Для этого достаточно взять данные из kadr и sledKadr кадров и в зависимости от timeKadr подсчитать значение.

Смысл такой: у нас есть допустим два кадра 15-тый и 16-тый. Есть там какой-то сустав. У этого сустава есть повороты и перемещения. Когда timeKadr=0, то повороты и перемещения берутся из 15-го кадра, а когда timeKadr=1, то всё берётся из 16-го кадра.

А вот когда timeKadr находится между 0 и 1, то мы подсчитываем "среднее значение".

Вот есть у нас поворот 30 градусов в 15 кадре и 45 градусов в 16-том. Мы должны плавно это значение менять. Для этого и есть переменная timeKadr:

30+(45-30)*timeKadr;

Когда timeKadr = 0, то поворот будет 30 градусов.
Когда timeKadr = 0.1, то поворот будет 31.5 градусов.
Когда timeKadr = 0.2, то поворот будет 33 градусов.
Когда timeKadr = 0.3, то поворот будет 34.5 градусов.
Когда timeKadr = 0.4, то поворот будет 36 градусов.
Когда timeKadr = 0.5, то поворот будет 37.5 градусов.
Когда timeKadr = 0.6, то поворот будет 39 градусов.
Когда timeKadr = 0.7, то поворот будет 40.5 градусов.
Когда timeKadr = 0.8, то поворот будет 42 градусов.
Когда timeKadr = 0.9, то поворот будет 43.5 градусов.
Когда timeKadr = 1, то поворот будет 45 градусов.

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

tempPovorot:TPovorot; - для поворота
tempPeremesh:TPeremesh; - для перемещения

А вот как будет выглядеть описанное выше "30+(45-30)*timeKadr;":

TempPovorot[0]:=anim.kadri[kadr].povorot[i][0]+(anim.kadri[sledKadr].povorot[i][0]-anim.kadri[kadr].povorot[i][0])*timeKadr;
TempPovorot[1]:=anim.kadri[kadr].povorot[i][1]+(anim.kadri[sledKadr].povorot[i][0]-anim.kadri[kadr].povorot[i][1])*timeKadr;
TempPovorot[2]:=anim.kadri[kadr].povorot[i][2]+(anim.kadri[sledKadr].povorot[i][0]-anim.kadri[kadr].povorot[i][2])*timeKadr;
TempPeremesh[0]:=anim.kadri[kadr].peremesh[i][0]+(anim.kadri[sledKadr].peremesh[i][0]-anim.kadri[kadr].peremesh[i][0])*timeKadr;
TempPeremesh[1]:=anim.kadri[kadr].peremesh[i][1]+(anim.kadri[sledKadr].peremesh[i][0]-anim.kadri[kadr].peremesh[i][1])*timeKadr;
TempPeremesh[2]:=anim.kadri[kadr].peremesh[i][2]+(anim.kadri[sledKadr].peremesh[i][0]-anim.kadri[kadr].peremesh[i][2])*timeKadr;

Теперь про кватернионы.

Среди загрузчиков есть файл _math.pas - там кватернионы эти и плавность.
Смысл там точно такой же, как и без кватернионов.
Процедурой fromAngles(angles:TVector3s) делается кватернион.
Процедурой slerp(q1,q2:quaternion;interp:single) - плавный переход между двумя кватернионами. timeKadr здесь вот где будет:
slerp(Q1,Q2,timeKadr);

С кватернионами всё выходит менее криво. Это связано с тем, что без кватернионов мы линейно меняем проекции углов, что неправильно.