21/src/Animation.cpp

138 lines
5.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}
}