138 lines
5.6 KiB
C++
138 lines
5.6 KiB
C++
#include "Animation.h"
|
||
|
||
#include <GLM/gtx/compatibility.hpp>
|
||
|
||
// Выполнить анимацию для канала с учетом времени относительно начала
|
||
void Channel::process(float dtime, ANIM_ENDINGS endings)
|
||
{
|
||
// Если указатель на узел не пустой и есть ключевые кадры
|
||
if (target && timestamps.size() && timestamps.size() == values.size())
|
||
{
|
||
// Если анимация зациклена
|
||
if (endings == CYCLED)
|
||
{
|
||
// Получаем общую длительность анимации
|
||
float totalDuration = timestamps.back();
|
||
// Обновляем dtime для зацикливания
|
||
if (totalDuration > 0)
|
||
{
|
||
dtime = fmod(dtime, totalDuration);
|
||
}
|
||
}
|
||
|
||
// Итоговое значение параметра
|
||
PARAMETER_TYPE parameterValue;
|
||
// Если только один кадр, используем значение этого кадра
|
||
if (timestamps.size() == 1)
|
||
{
|
||
// Применяем значение к целевому узлу
|
||
parameterValue = values[0];
|
||
}
|
||
else
|
||
{
|
||
// Поиск подходящих индексов для интерполяции
|
||
size_t index1 = 0, index2 = 1;
|
||
for (; index2 < timestamps.size(); ++index2)
|
||
{
|
||
if (timestamps[index2] >= dtime)
|
||
{
|
||
break;
|
||
}
|
||
index1 = index2;
|
||
}
|
||
|
||
// Если время выходит за рамки последнего кадра, используем значение в зависимости от поведения при окончании анимации
|
||
if (index2 == timestamps.size())
|
||
{
|
||
if (endings == STOP)
|
||
parameterValue = values.back();
|
||
else
|
||
if (endings == TO_BEGIN)
|
||
parameterValue = values.front();
|
||
}
|
||
else
|
||
{
|
||
// Вычисляем коэффициент интерполяции
|
||
float t = (dtime - timestamps[index1]) / (timestamps[index2] - timestamps[index1]);
|
||
|
||
// Выбор метода интерполяции и интерполяция
|
||
switch (interpolation)
|
||
{
|
||
case STEP:
|
||
parameterValue = values[index1];
|
||
break;
|
||
case LINEAR:
|
||
if (path == ROTATION)
|
||
parameterValue.quat = glm::slerp(values[index1].quat, values[index2].quat, t);
|
||
else
|
||
parameterValue.vec3 = glm::lerp(values[index1].vec3, values[index2].vec3, t);
|
||
break;
|
||
case CUBICSPLINE:
|
||
float t2 = t*t;
|
||
float t3 = t2*t;
|
||
if (path == ROTATION)
|
||
{
|
||
// Обеспечение кратчайшего пути для интерполяции
|
||
glm::quat q2Adjusted = values[index2]. quat;
|
||
glm::quat t2Adjusted = tangents[index2].in. quat;
|
||
if (glm::dot(values[index1].quat, values[index2].quat) < 0)
|
||
{
|
||
q2Adjusted = -q2Adjusted;
|
||
t2Adjusted = -t2Adjusted;
|
||
}
|
||
parameterValue.quat = (2*t3 - 3*t2 + 1) * values[index1]. quat
|
||
+ (t3 - 2*t2 + t) * tangents[index1].out.quat
|
||
+ (-2*t3 + 3*t2) * q2Adjusted
|
||
+ (t3 - t2) * t2Adjusted;
|
||
parameterValue.quat = glm::normalize(parameterValue.quat);
|
||
}
|
||
else
|
||
parameterValue.vec3 = (2*t3 - 3*t2 + 1) * values[index1]. vec3
|
||
+ (t3 - 2*t2 + t) * tangents[index1].out.vec3
|
||
+ (-2*t3 + 3*t2) * values[index2]. vec3
|
||
+ (t3 - t2) * tangents[index2].in. vec3;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (path == POSITION)
|
||
target->e_position() = parameterValue.vec3;
|
||
if (path == ROTATION)
|
||
target->e_rotation() = parameterValue.quat;
|
||
if (path == SCALE)
|
||
target->e_scale() = parameterValue.vec3;
|
||
|
||
}
|
||
}
|
||
|
||
// Задает состояние анимации как начало
|
||
void Animation::begin()
|
||
{
|
||
begin_time = std::chrono::steady_clock::now();
|
||
enabled = true;
|
||
}
|
||
|
||
// Заканчивает выполнение анимации
|
||
void Animation::end()
|
||
{
|
||
enabled = false;
|
||
}
|
||
|
||
// Возвращает состояние анимации (вкл/выкл)
|
||
bool Animation::isEnabled()
|
||
{
|
||
return enabled;
|
||
}
|
||
|
||
// Выполняет анимацию для всех каналов
|
||
void Animation::process()
|
||
{
|
||
float dtime = std::chrono::duration<float, std::milli>(std::chrono::steady_clock::now() - begin_time).count() / 1000.0f;
|
||
for (Channel & channel : channels)
|
||
{
|
||
// channel.interpolation = STEP;
|
||
channel.process(dtime, endings);
|
||
}
|
||
}
|